Учитывая следующее:
std::string make_what_string( const std::string &id );
struct basic_foo
{
basic_foo( std::string message, std::string id );
};
struct foo
: public basic_foo
{
foo::foo( std::string id)
: basic_foo( make_what_string( id ), std::move( id ) ) // Is this valid?
{
}
};
Поскольку порядок оценки параметров в C ++ не определен, мне интересно,
линия
basic_foo( make_what_string( id ), std::move( id ) )
в приведенном выше коде действителен.
я знаю это std::move
это не что иное, как приведение, но когда есть std :: string
ход ctor выполняется? После того, как все аргументы были оценены и пришло время позвонить
базовый конструктор? Или это делается при оценке параметров? В
другие слова:
Компилятор делает это:
std::string &&tmp2 = std::move(id);
std::string tmp1 = make_what_string(id);
basic_foo(tmp1, tmp2);
который действителен. Или это:
std::string tmp2 = std::move(id);
std::string tmp1 = make_what_string(id);
basic_foo(tmp1, tmp2);
который недействителен. Обратите внимание, что в обоих случаях порядок является «неожиданным».
Смотрите раздел 1.9:
За исключением отмеченных случаев, оценки операндов отдельных операторов и подвыражений отдельных выражений не являются последовательными.
а также
При вызове функции (независимо от того, является ли функция встроенной), каждое вычисление значения и побочный эффект, связанный с любым выражением аргумента или с выражением постфикса, обозначающим вызываемую функцию, упорядочивается перед выполнением каждого выражения или оператора в теле вызываемая функция. [ Заметка: Вычисления значений и побочные эффекты, связанные с различными выражениями аргументов, не являются последовательными. —конечная нота ]
Я думаю, что проблема заключается в том, что не очень ясно, считается ли инициализация параметров побочным эффектом, связанным с выражениями аргументов. Тем не менее, кажется, что это подкреплено разделом 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
остается в допустимом состоянии с неопределенным значением.
Таким образом, у вас нет неопределенного поведения, у вас есть неопределенное поведение.
Это не совсем верно. Порядок оценки аргументов функции не указан. Другими словами, вы не знаете, будет ли компилятор выбирать эту последовательность:
tmp1 = make_what_string(id);
tmp2 = std::move(id);
basic_foo(tmp1, tmp2);
или этот:
tmp1 = std::move(id);
tmp2 = make_what_string(id); //id has already been moved from!
basic_foo(tmp2, tmp1);