Ответ 1
См. раздел 1.9:
За исключением тех случаев, когда отмечено, оценки операндов отдельных операторов и подвыражений отдельных выражений не подвержены.
и
При вызове функции (независимо от того, является ли функция встроенной) каждое вычисление значения и побочный эффект, связанный с любым выражением аргумента, или с выражением postfix, обозначающим вызываемую функцию, секвенируются перед выполнением каждого выражения или оператора в тело вызываемой функции. [Примечание. Вычисления значений и побочные эффекты, связанные с разными выражениями аргументов, не имеют никакого значения. -end note]
Я думаю, проблема в том, что не очень ясно, является ли инициализация параметров рассмотрением побочного эффекта, связанного с выражениями аргументов. Однако, как представляется, он подпадает под действие раздела 5.2.2:
Инициализация и уничтожение каждого параметра происходит в контексте вызывающей функции.
И там также примечание в том же абзаце, что делает его немного яснее:
Когда вызывается функция, каждый параметр (8.3.5) должен быть инициализирован (8.5, 12.8, 12.1) с соответствующим аргументом. [Примечание: такие инициализации неопределенно секвенированы относительно друг друга (1.9) - примечание конца)
Итак, да, инициализация аргументов неопределенно упорядочена относительно друг друга. Инициализация может происходить в любом из этих ордеров:
std::string message = make_what_string(id);
std::string id = std::move( id );
std::string id = std::move( id );
std::string message = make_what_string(id);
Во втором случае make_what_string
завершает работу с перенесенной строкой.
Итак, хотя std::move
фактически ничего не движет, важно то, что фактическое перемещение также не зависит от другого аргумента.
Определение конструктора перемещения basic_string(basic_string&& str)
гласит:
[...]
str
остается в допустимом состоянии с неопределенным значением.
Итак, у вас нет поведения undefined, у вас есть неопределенное поведение.