15

当我使用编译以下代码时g++

class A {};

void foo(A&) {}

int main()
{
  foo(A());
  return 0;
}

我收到以下错误消息:

> g++ test.cpp -o test     
test.cpp: In function ‘int main()’:
test.cpp:10: error: invalid initialization of non-const reference of type ‘A&’ from a temporary of type ‘A’
test.cpp:6: error: in passing argument 1 of ‘void foo(A&)’

经过一番思考,这些错误对我来说很有意义。A()只是一个临时值,而不是堆栈上的可分配位置,因此它似乎没有地址。如果它没有地址,那么我就无法引用它。好的。

可是等等!如果我将以下转换运算符添加到类A

class A
{
public:
  operator A&() { return *this; }
};

那么一切都很好!我的问题是这是否远程安全。什么this时候A()被构造为临时值?

我对以下事实充满信心

void foo(const A&) {}

可以根据g++我使用的所有其他编译器接受临时值。const关键字总是可以被抛弃,所以如果const A&参数和参数之间存在任何实际的语义差异,我会感到惊讶A&。所以我想这是问我问题的另一种方式:为什么const对编译器认为安全的临时值的引用而不是非const引用?

4

3 回答 3

17

并不是不能获取地址(编译器总是可以命令将其推入堆栈,它使用 ref-to-const 执行此操作),这是程序员意图的问题。使用带有 A& 的接口,它表示“我将修改此参数中的内容,以便您可以在函数调用后阅读”。如果你给它一个临时的,那么它“修改”的东西在函数之后就不存在了。这(可能)是一个编程错误,因此是不允许的。例如,考虑:

void plus_one(int & x) { ++x; }

int main() {
   int x = 2;
   float f = 10.0;

   plus_one(x); plus_one(f);

   cout << x << endl << f << endl;
}

这不会编译,但如果临时对象可以绑定到 ref-to-non-const,它会编译但会产生令人惊讶的结果。在 plus_one(f) 中,f 将被隐式转换为临时 int,plus_one 将获取 temp 并递增它,而底层浮点 f 保持不变。当 plus_one 返回时,它没有任何效果。这几乎肯定不是程序员的本意。


规则偶尔会搞砸。一个常见的例子(描述here)是试图打开一个文件,打印一些东西,然后关闭它。你希望能够做到:

ofstream("bar.t") << "flah";

但是你不能因为 operator<< 需要一个 ref-to-non-const。您的选择是将其分成两行,或调用返回 ref-to-non-const 的方法:

ofstream("bar.t").flush() << "flah";
于 2009-06-24T21:04:54.740 回答
5

当您将 r 值分配给 const 引用时,您可以保证在引用被销毁之前临时不会被销毁。当您分配给非常量引用时,不会做出这样的保证。

int main()
{
   const A& a2= A(); // this is fine, and the temporary will last until the end of the current scope.
   A& a1 = A(); // You can't do this.
}

你不能安全地抛弃 const-ness 并期望一切正常。const 和非常量引用有不同的语义。

于 2009-06-24T21:04:23.990 回答
3

有些人可能会遇到的一个问题:MSVC 编译器(Visual Studio 编译器,已通过 Visual Studio 2008 验证)毫无问题地编译此代码。我们一直在一个项目中使用这种范例来处理通常需要一个参数(要消化的数据块)的函数,但有时希望搜索该块并将结果返回给调用者。另一种模式是通过三个参数启用的——第二个参数是要搜索的信息(默认引用空字符串),第三个参数是返回数据(默认引用所需类型的空列表)。

此范例在 Visual Studio 2005 和 2008 中有效,我们必须对其进行重构,以便构建并返回列表,而不是调用者拥有和变异以使用 g++ 编译。

如果有办法将编译器开关设置为在 MSVC 中禁止这种行为或在 g++ 中允许这种行为,我会很高兴知道;MSVC 编译器的允许性/g++ 编译器的限制性增加了移植代码的复杂性。

于 2009-08-09T16:34:47.207 回答