В C ++ 11, обычной практикой является передача lvalue в функцию по ссылке.
int& f(int& a){
return a;
}
int main(void){
auto a = 1;
auto b = f(a);
return 0;
}
Однако возможно ли передать значение в функцию по ссылке rvalue и вернуть это значение через lvalue?
int& f(int&& a){
return a;
}
int main(void){
auto b = f(1);
return 0;
}
Почему это или почему это невозможно?
Это возможно, но обычно неразумно. Этот код в порядке:
#include <utility>
#include <iostream>
int &foo(int &&a) {
return a;
}
int main() {
int a = 1;
std::cout << foo(std::move(a)) << "\n";
}
Этот код тоже в порядке:
int main() {
std::cout << foo(1) << "\n";
}
Этот код имеет неопределенное поведение:
int main() {
int &a = foo(1); // lifetime of the temporary 1 ends "at the semi-colon"std::cout << a << "\n";
}
Таким образом, довольно легко неправильно использовать функцию foo
,
Что касается причины, по которой это работает — все, что здесь происходит, это неявное преобразование из ссылки на rvalue в ссылку на lvalue. Было бы неудобно, если бы это не было разрешено, потому что это означало бы, например, что вы не можете написать:
void bar1(const int &a) {
std::cout << (a + 1) << "\n";
}
void bar2(int &&a) {
bar1(a);
... do destructive stuff with a ...
}
Могут быть более веские причины для разрешения неявного преобразования. Я не знаю официальной мотивации, это только первое, о чем я подумал.
Даже в C ++ 03 была связанная проблема. Ты можешь написать:
const int *baz(const int &a) { return &a; }
чтобы получить указатель на временное / значение — то, что язык запрещает вам делать напрямую и которое ведет к тому же неопределенному поведению, если возвращаемое значение переживает выражение, в котором оно было создано.
Вы могли бы сказать, что запрещающий &1
(взятие указателя на литерал или другое временное значение) — это то место, в котором стандарт C ++ не просто предположим, что программист знает, что они делают. Но я думаю, что исторически обоснование этого заключается в том, что вы иметь чтобы иметь возможность получить постоянную ссылку на временную переменную, чтобы перегрузка оператора работала. Вы не должны иметь возможность взять указатель на временный объект, и C запрещает это, потому что в C целочисленный литерал никогда не должен иметь место в памяти. Таким образом, C ++ продолжает запрещать это, хотя в C ++, когда вы берете ссылку на целочисленный литерал, компилятор может быть вынужден создать фактическое местоположение, содержащее это значение. Страуструп наверное мог сказали то же самое о получении указателя на целочисленный литерал, но в этом не было необходимости.
Если вы вступаете во владение, вы несете ответственность за объект. Если вы не сохраните его где-то, это все равно, что вернуть ссылку на временный файл; когда область действия функции заканчивается, объект, которым вы «владеете», уничтожается, и поэтому возвращаемая ссылка перестает быть действительной.