Почему инициализация неконстантного & из rvalue недопустима?

Взглянем на следующий код:

#include<string>
#include<iostream>
using T = std::string;


void baz(T& v)       { std::cout << v << std::endl; }
void boo(const T& v) { std::cout << v << std::endl; }

int main() {
        baz( T("Passing an rvalue to be assigned to a lvalue&") ); //<--- This call is invalid
        boo( T("Passing an rvalue to be assigned to a constant lvalue&") );
}

Стандарт сообщает нам, что первый вызов функции недействителен по очевидной причине, но второй вызов функции недействителен, потому что его можно интерпретировать как «Сгенерировать переменную, привязанную к области видимости функции boo, которая содержит lvalue, равное переданному rvalue. к функции ". По крайней мере, насколько я понимаю стандарт.

Однако добавление вспомогательных функций, таких как:

constexpr T &to_lvalue(T &&value) noexcept { return value; }

И вызываем функцию baz, используя ее для переноса параметров:

baz( to_lvalue( T("Passing a rvalue which will be transformed into an lvalue reference bound to the scope of baz") ) );

Считается совершенно правильным кодом и (насколько я понимаю) он в основном приводит к одному и тому же, а именно к построению lvalue, привязанного к области baz

Так почему бы компиляции просто не сгенерировать для вас этот код при вызове baz с rvalue? Он не требует накладных расходов во время выполнения, и поведение, кажется, хорошо определено. Что-то мне здесь не хватает? Или стандарт определен таким образом, чтобы не «по ошибке» использовать функцию, которая, как ожидается, изменит значение?


person George    schedule 29.08.2017    source источник
comment
Ваш совершенно действительный код - UB. Временное значение, переданное to_lvalue, истекает после возврата to_lvalue, оставляя вам висящую ссылку.   -  person nwp    schedule 29.08.2017
comment
to_lvalue() возвращает ссылку на временный объект, который выходит за пределы области видимости и уничтожается при возврате to_lvalue(). Таким образом, это неопределенное поведение.   -  person Sam Varshavchik    schedule 29.08.2017
comment
Разве жизнь временно не продлена до конца заявления?   -  person rustyx    schedule 29.08.2017
comment
@nwp Значит, в этом случае нарушена реализация в gcc / clang? Поскольку код, кажется, дает ожидаемый результат, и при его компиляции нет предупреждений   -  person George    schedule 29.08.2017
comment
похоже дает ожидаемый результат Вот что интересно с UB. Это может сработать, а может и нет, иногда оно может работать, а также может начать ломаться, когда вы измените что-то совершенно не связанное с этим.   -  person Borgleader    schedule 29.08.2017
comment
Не сломано. Делая именно то, что вы хотели, он допускал неопределенное поведение. Вы можете использовать Asan и UBsan, чтобы получить ошибку, или использовать -O3, чтобы попытаться создать мусор. Идея UB заключается в том, что компилятору все равно.   -  person nwp    schedule 29.08.2017
comment
clang ++ -std = c ++ 1z -O3 -fsanitize = undefined main.cpp По-прежнему дает тот же результат, то же самое с gcc   -  person George    schedule 29.08.2017
comment
@nwp в редких случаях это действительно волнует   -  person W.F.    schedule 29.08.2017
comment
@RustyX, это тоже было моим пониманием. Хотел бы найти что-нибудь в стандарте, чтобы процитировать. Тестирование FWI на MSVC 2015, похоже, так и есть, сохранение в T& temp с последующим вызовом baz в следующем операторе приводит к разрушенному объекту (MSVC установил его размер равным нулю, но более творческий код может вызвать сбой)   -  person Fire Lancer    schedule 29.08.2017
comment
В этом очень конкретном случае я считаю, что с вами все в порядке, потому что T("Passing an rvalue to be assigned to a lvalue&") доживает до конца выражения. Если сделать два шага, взорвется. Создание to_lvalue приведет к еще большему количеству проблем, ИМХО. Ошибку компилятора исправить намного проще, чем случайные сбои из-за недиагностированного UB.   -  person NathanOliver    schedule 29.08.2017
comment
@NathanOliver Я немного изменил ваш код, чтобы сделать его более читабельным и вызвать ошибку: coliru.stacked- crooked.com/a/18edb30f599986f9 Мне удалось получить предупреждение во время выполнения, но оно все еще работает и дает ожидаемый результат, несмотря на то, что объект был уничтожен до вызова функции oO   -  person George    schedule 29.08.2017
comment
Однако следует отметить, что при использовании функции, как я показал: coliru.stacked-crooked.com / a / e32a119c75f2e881, хотя все еще вызывает предупреждение, похоже, привязывает переменную к области видимости baz, поскольку деструктор вызывается только тогда, когда baz существует   -  person George    schedule 29.08.2017
comment
Какая часть неопределенного поведения означает, что все может случиться, неясно? Все может случиться означает, что: 1) компилятор может сгенерировать код, который вы намереваетесь сгенерировать, 2) компилятор вообще не может скомпилировать код, 3) компилятор генерирует совершенно неправильный код, или 4) всю жизнь, как вы ее знаете. подходит к концу, и каждая молекула взрывается со скоростью света, просто чтобы назвать некоторые из возможностей.   -  person Sam Varshavchik    schedule 29.08.2017
comment
@SamVarshavchik Если вы считаете, что ценность не гарантирована, чтобы жить до конца утверждения, то ответьте, в идеале, со ссылкой на стандарт.   -  person Fire Lancer    schedule 29.08.2017
comment
@ Джордж Сложно сказать. С одной стороны, у вас есть это временное значение, живущее до конца выражения, с другой - у вас есть, что, когда привязанная к ним ссылка выходит за пределы области видимости, временное уничтожается. Я не знаю, превосходит ли последнее в данном случае первое, и это то, что нам нужно знать, чтобы узнать, UB это или нет.   -  person NathanOliver    schedule 29.08.2017
comment
@NathanOliver Я не думаю, что ни gcc, ни clang думаю, что в коде есть UB   -  person W.F.    schedule 29.08.2017
comment
@ W.F. Да, код компилируется на всех платформах. Это еще не значит, что это не неопределенное поведение. На самом деле мы сможем узнать наверняка только с помощью ответа language-lawyer. Я склонен полагать, что в этом случае все в порядке, потому что временные объекты живут до конца полного выражения.   -  person NathanOliver    schedule 29.08.2017
comment
@NathanOliver в соответствии с [expr.const # 2.6] моим вариантом код из предыдущего комментария должен намеренно спровоцировать компилятор на проверку того, продлил ли такой временный срок время жизни до конца выражения. Или я что-то упускаю ...?   -  person W.F.    schedule 29.08.2017
comment
@ W.F. Спасибо. Это интересно. Я не уверен, считается ли преобразование ссылки операцией или выражением. Если последнее, то все должно быть в порядке.   -  person NathanOliver    schedule 29.08.2017