Подтвердить что ты не робот

Перенос семантики и порядка оценки аргументов

Учитывая следующее:

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?
    {
    }
};

Поскольку порядок оценки параметров в С++ не указан, мне интересно, если строка

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);

что является недопустимым. Обратите внимание, что в обоих случаях заказ является "неожиданным", один.

4b9b3361

Ответ 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, у вас есть неопределенное поведение.

Ответ 2

Это неправда. Порядок оценки аргументов функции не указан. Другими словами, вы не знаете, будет ли компилятор выбирать эту последовательность:

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);