无法传递临时对象作为参考

时间:2014-12-13 21:26:26

标签: c++ visual-c++ c++11

这是一个非常小的例子:

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)

我找到了三个解决方法:

  1. 避免使用内联临时变量来调用ProcessFoo。
  2. 像这样:

    Foo foo42(42);
    ProcessFoo(foo42);
    
    1. ProcessFoo采用const引用:void ProcessFoo(const Foo& foo)

    2. ProcessFoo只是让Foo按值传递。 void ProcessFoo(Foo foo)

    3. 为什么编译器禁止我的原始代码? (这是什么防守)?上面三个满足编译器的解决方法中的每一个都有什么用? MSVC会允许什么,但不是g ++?

4 个答案:

答案 0 :(得分:13)

按照设计,C ++只允许将临时值传递给const引用,值或右值引用。这个想法是一个采用非const引用参数的函数声明它想要修改参数并允许它返回给调用者。暂时这样做是没有意义的,很可能是错误。

我不知道你正在运行什么版本的g ++。它在这里不起作用:http://coliru.stacked-crooked.com/a/43096cb398cbc973

答案 1 :(得分:9)

  

为什么编译器禁止我的原始代码?

因为标准禁止这样做:

  

8.5.3参考文献5
  ...
  否则,引用应是对非易失性const类型的左值引用(即,cv1应为   const),或参考应为右值参考   [例子:
  双&安培; rd2 = 2.0; //错误:不是左值,而是引用不是const
  ...

'

  

它有什么防范?

无意中修改了函数调用后将要销毁的对象。

  

满足编译器的上述三种解决方法中的每一种都有什么用呢?

1创建一个命名对象,并3创建一个副本 2正常工作,因为对象的生命周期只是被扩展,并且同时防止对它的更改。

  

MSVC允许什么,但不是g ++?

因为它是一种语言扩展。通过转到Property Pages->C/C++->Language->Disable Language Extensions禁用它,您将收到错误。

答案 2 :(得分:2)

将ProcessFoo的原型声明为

void ProcessFoo(Foo& foo)

您正在传达您的意图作为正式参数" foo"可以修改,因为它没有被const&amp ;.传递。

在通话现场,

ProcessFoo(Foo(42));

Foo(42)正在创建一个不可修改的临时堆栈对象。可以通过值传递或将ref-to-const传递给方法。

正如您自己列出的那样,满足这些约束,使编译器感到高兴。

  1. 为您提供一个非编译器生成且受您控制的对象。
  2. 通知编译器该方法保证了对象的常量。
  3. 通知编译器此(临时)对象是按值传递的,因此没有问题。

答案 3 :(得分:2)

  

为什么编译器禁止我的原始代码?

MSVC有一个扩展,允许临时绑定到非const左值引用。当然,这不是一个符合标准的功能,所以我会远离它以便携带。例如,它并没有像您所见过的那样使用最新版本的GCC和Clang。

  

满足编译器的上述三种解决方法中的每一种都是什么?

回到C ++ 03,表达式只能是左值或左值。参考文献只能指定"左值"一个对象,因此它用于别名预先存在的对象。相比之下,rvalues不会出现在它们出现的表达之外。此外,引用的最终结果通常是复制或修改对象,例如,修改像55这样的右值对语言没有多大意义。

规则允许您将rvalue绑定到对const的lvalue-reference,在这种情况下,临时生命周期延长到引用的生命周期。按值获取对象时,将复制该对象。

使用C ++ 11,我们有rvalue-references和xvalues,它们是为了交换所有权而制作的。这样可以减少lvalue-references对const的有用性。此外,如果它是一个右值,则取值依次会导致移动。