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

sel*_*bie 17 c++ visual-c++ c++11

这是一个非常小的例子:

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

void ProcessFoo(Foo& foo)
{
}

int main()
{
    ProcessFoo(Foo(42));
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

以上编译在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)
Run Code Online (Sandbox Code Playgroud)

我找到了三个解决方法:

  1. 避免使用内联临时变量来调用ProcessFoo.

像这样:

Foo foo42(42);
ProcessFoo(foo42);
Run Code Online (Sandbox Code Playgroud)
  1. ProcessFoo采用const引用: void ProcessFoo(const Foo& foo)

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

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

Jay*_*ler 15

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

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

  • @ddriver你提到的情况是创建rvalue引用的原因,我们在那里声明作为接口的一部分,我们将修改临时传递,没有人会看到副作用.语言设计者觉得有太多方法可以犯错误,允许临时语言绑定到非const引用.如果你按值获取参数并传递一个临时值,大多数编译器都会忽略副本,因此使用非const引用就不会有效率增益. (2认同)

imr*_*eal 10

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

因为标准禁止这样做:

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

"

什么是防守?

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

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

1创建命名对象和3副本.
2之所以有效,是因为只是扩展了对象的生命周期,同时防止了对它的更改.

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

因为它是语言扩展.通过转到禁用它Property Pages->C/C++->Language->Disable Language Extensions,你会得到一个错误.