3

此代码无法编译:

class C {};

void foo (C& c) {}

C bar() { return C(); }

int main()              
{
  foo(bar());
}               

行中的编译错误(GCC 4.1.2)foo(bar())

从“C”类型的临时对象对“C&”类型的非常量引用进行无效初始化

bar()返回一个mutable对象时,它应该编译......
为什么 C++ 不允许上面的代码?


编辑:我在所有答案中总结了所有好主意的答案;-)

4

8 回答 8

13

此处适用的规则是您不能创建对临时对象的非常量引用。如果foo被声明为foo(const C&)代码就可以了。

但是,临时对象本身不是 const;您可以在其上调用非常量成员函数,例如bar().non_const_member_function().

使用 C++11,可以编写 foo 来获取右值引用;在这种情况下,通话就可以了:

void foo(C&&);
foo(bar());  // okay
于 2012-11-14T19:13:46.553 回答
8

这是因为返回的值bar是一个临时值。由于它的存在是暂时的,因此您不能使用指针或对其的引用。

但是,如果您存储该临时对象的副本,就像在您的第二次更改中一样,您不再将对临时对象的引用传递给foo,而是对真实有形对象的引用。在第一种情况下,当您更改为对常量对象的引用时,编译器会确保临时对象保持足够长的时间(根据 C++ 规范)。

于 2012-11-14T19:12:27.633 回答
3

可修改(左值)引用不绑定到临时值。但是,常量引用确实绑定到临时值。与value返回的对象是否为const无关;这只是表达式是否是临时的问题。

例如,以下有效的:

struct C { void i_am_non_const() {} };

int main()
{
    bar().i_am_non_const();
}
于 2012-11-14T19:13:01.130 回答
3

问题不在于 的声明,bar而在于的声明foofoo接受非const引用,并且临时对象只能绑定到const引用(然后扩展临时对象的生命周期以匹配它绑定到的引用的生命周期)。

允许非const引用绑定到临时没有多大意义。非const引用意味着它将修改绑定到它的任何对象。修改临时文件没有任何意义,因为它的生命周期是有限的,一旦超出范围,更改就会丢失。

于 2012-11-14T19:13:16.900 回答
1

这是一种设计选择。这里没有什么是天生不可能的。只是一个设计选择。

在 C++11 中,您有第三种选择,它也是更好的选择:

void foo(C && c) {} 

也就是说,使用右值引用。

于 2012-11-14T19:13:53.757 回答
1

它不是 const,但它是一个临时的rvalue。因此,它不能绑定到非常量左值引用。

它可以绑定到 const 或rvalue引用,并且您可以在其上调用成员函数(无论是否为 const):

class C { void f(); };

void foo_const(C const &);
void foo_rvalue(C &&);

foo_const( bar() );  // OK
foo_rvalue( bar() ); // OK
bar().f();           // OK
于 2012-11-14T19:15:35.963 回答
0

真正的硬道理是,获取对临时值的引用是没有意义的。

通过引用传递对象的要点是它允许您修改其状态。但是,在临时的情况下,就其本质而言,能够对其进行修改并不是特别有帮助,因为您无法在以后的代码中获得对它的另一个引用以查看更改。

但是,如果您有const参考,这会有所不同。由于您只会从const参考资料中阅读,因此能够在那里使用临时文件是完全有意义的。这就是为什么编译器会为您“破解”它,并为您想要“转换”为const引用的临时对象提供更永久的地址。

因此,规则是您不能获得const对临时值的非引用。(这在 C++11 中略有改变,我们有一种新类型的引用来服务于这个确切的目的,但方法应该以特殊的方式处理这些。)

于 2012-11-14T19:15:05.633 回答
0

谢谢大家的回答:-)
在这里我收集你的好主意;-)

回答

按值返回 不是const。例如,我们可以调用return by value的非常量成员函数:

class C { 
  public:
    int x;
    void set (int n) { x = n; }  // non-const function
};

C bar()  { return C(); }

int main ()
{
    bar.set(5); // OK
}

但是C++ 不允许对临时对象进行非常量引用。但是 C++11 允许对临时对象
进行非 const右值引用。;-)

解释

class C {};

void foo (C& c) {}

C bar()  { return C(); }
//bar() returns a temporary object
//temporary objects cannot be non-const referenced

int main()
{
  //foo() wants a mutable reference (i.e. non-const)
  foo( bar() ); // => compilation error     
}                 

三个修复

  1. 变更foo声明

      void foo (const C& c) {}
    
  2. 使用另一个对象

      int main()              
      {
        C c;
        foo( c = bar() );
      }
    
  3. 使用 C++11右值引用

      void foo(C && c) {}
    

而且

为了确认临时对象是 const,上面的源代码由于同样的原因而失败:

class C {};

void foo(C& c) {}

int main()                
{
  foo( C() );
}            
于 2012-11-14T19:23:04.610 回答