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

Нельзя передать временный объект в качестве ссылки

Это очень минимальный пример:

class Foo
{
public:
    Foo(int x) {};
};

void ProcessFoo(Foo& foo)
{
}

int main()
{
    ProcessFoo(Foo(42));
    return 0;
}

Вышеописанные компилируются в Visual Studio, но генерируют ошибку в Linux и Mac.

Компиляция приведенного выше генерирует это:

$ g++ -std=c++11 -c newfile.cpp

newfile.cpp: In function ‘int main()’:
newfile.cpp:23:23: error: invalid initialization of non-const reference of type ‘Foo&’ from an rvalue of type ‘Foo’
     ProcessFoo(Foo(42));
                       ^
newfile.cpp:14:6: note: in passing argument 1 of ‘void ProcessFoo(Foo&)’
 void ProcessFoo(Foo& foo)

Я нашел три метода обхода:

  • Избегайте использования встроенной переменной temp для вызова ProcessFoo.

Вот так:

Foo foo42(42);
ProcessFoo(foo42);
  1. ProcessFoo принимает ссылку на const: void ProcessFoo(const Foo& foo)

  2. ProcessFoo просто позволяет Foo пройти по значению. void ProcessFoo(Foo foo)

Почему компилятор запрещает мой исходный код? (О чем это защищает)? Что касается каждого из трех обходных решений, которые удовлетворяют компилятору? Что бы MSVC разрешил, но не g++?

4b9b3361

Ответ 1

По дизайну С++ разрешает только временную передачу в ссылку на константу, значение или значение rvalue. Идея заключается в том, что функция, принимающая неконстантный ссылочный параметр, заявляет, что хочет изменить параметр и позволить ему вернуться к вызывающему. Выполнение этого с помощью временного значения бессмысленно и, скорее всего, является ошибкой.

И я не знаю, какую версию g++ вы используете. Здесь это не работает: http://coliru.stacked-crooked.com/a/43096cb398cbc973

Ответ 2

Почему компилятор запрещает мой исходный код?

Потому что это запрещено стандартом:

8.5.3 Список литературы 5
...
В противном случае ссылка должна быть ссылкой lvalue на нелетучий const-тип (то есть cv1 должен быть const), или ссылка должна быть ссылкой rvalue.
[Пример:
двойной & rd2 = 2,0;//ошибка: не lvalue и ссылка not const
...

Чем он защищен?

Непреднамеренно модифицирует объект, который будет разрушен после вызова функции.

Что происходит с каждым из трех обходных решений, которые удовлетворяют компилятору?

1 Создает именованный объект и 3 копию.
2 Работает потому, что время жизни объекта просто расширено, и изменения в нем предотвращаются одновременно.

Что бы MSVC разрешил, но не g++?

Потому что это расширение языка. Отключите его, перейдя в Property Pages->C/C++->Language->Disable Language Extensions, и вы получите сообщение об ошибке.

Ответ 3

Как только вы объявили прототип для ProcessFoo как

void ProcessFoo(Foo& foo)

Вы передаете свое намерение, поскольку формальный параметр "foo" может быть изменен, поскольку он не передается функцией const &.

На сайте-вызове

ProcessFoo(Foo(42));

Foo (42) создает временный объект стека, который не модифицируется. Это нормально для передачи по значению или для передачи по методу pass-by-ref-to-const.

Как вы сами указали, удовлетворяя эти ограничения, делает компилятор счастливым.

  • предоставляет вам объект, который не сгенерирован компилятором и находится под вашим контролем.
  • Сообщает компилятору, что метод гарантирует константу объекта.
  • Сообщает компилятору, что этот (временный) объект передается по значению и, следовательно, никаких проблем.

Ответ 4

Почему компилятор запрещает мой исходный код?

MSVC имеет расширение, которое позволяет временным связям с не-const lvalue-ссылками. Конечно, это не стандартная функция, поэтому я бы держался подальше от нее, чтобы быть портативным. Например, он не работает с последними версиями GCC и Clang, как вы видели.

Что это за каждый из трех обходных решений, которые удовлетворяют компилятору?

В С++ 03 выражения могут быть только lvalues ​​или rvalues. Ссылки могли бы обозначать только "lvalueness" объекта, и поэтому он использовался с целью наложения ранее существовавшего объекта. Напротив, rvalues ​​не существуют за пределами выражения, в котором они появляются. Кроме того, конечный результат ссылок, как правило, заключался в том, чтобы копировать или изменять объект, и для языка не имеет особого смысла изменять значение r, например 55.

Правила позволяют привязать rvalue к lvalue-reference к const, и в этом случае временное время жизни увеличивается до времени жизни ссылки. Когда вы берете объект по значению, объект копируется.

С С++ 11 мы имеем rvalue-ссылки и xvalues, которые были сделаны для обмена собственностью. При этом уменьшается полезность lvalue-ссылок на const. Более того, при использовании значения value происходит перемещение, если оно является rvalue.