在C++中将非const引用传递给rvalues

Mat*_*ner 13 c++ gcc rvalue-reference c++11

在以下代码行中:

bootrec_reset(File(path, size, off), blksize);
Run Code Online (Sandbox Code Playgroud)

用原型调用函数:

static void bootrec_reset(File &file, ssize_t blksize);
Run Code Online (Sandbox Code Playgroud)

我收到此错误:

libcpfs/mkfs.cc:99:53:错误:从'File'类型的右值开始无效初始化'File&'类型的非const引用

libcpfs/mkfs.cc:30:13:错误:传递'void bootrec_reset(File&,ssize_t)'的参数1

我知道你不能const &根据标准将非const引用()传递给rvalues.但是,MSVC允许您这样做(请参阅此问题).这个问题试图解释为什么,但答案没有意义,因为他正在使用文字的引用,这是一个极端的案例,显然应该被禁止.

在给定的示例中,可以清楚地看到将发生以下事件顺序(就像在MSVC中一样):

  1. File的构造函数将被调用.
  2. 对堆栈的引用File,和blksize,被推入堆栈.
  3. bootrec_reset利用file.
  4. 从返回后bootrec_reset,临时File被摧毁.

有必要指出File引用需要是非const的,因为它是文件的临时句柄,在其上调用非const方法.此外,我不想将File构造函数参数传递bootrec_reset给那里构造,也没有任何理由File在调用者中手动构造和销毁对象.

所以我的问题是:

  1. 什么证明C++标准以这种方式禁止非const引用?
  2. 如何强制GCC允许此代码?
  3. 即将推出的C++ 0x标准是否会改变这一点,或者新标准是否在这里提供了更合适的内容,例如所有关于rvalue引用的乱码?

j_r*_*ker 8

是的,普通函数不能将非const引用绑定到临时函数 - 但方法可以 - 总是让我烦恼.TTBOMK的基本原理是这样的(源自这个comp.lang.c ++.主持线程):

假设你有:

 void inc( long &x ) { ++x; }

 void test() {
     int y = 0;
     inc( y );
     std::cout << y;
 } 
Run Code Online (Sandbox Code Playgroud)

如果你允许long &x参数inc()绑定到一个临时long副本y,那么这段代码显然不能达到预期的效果 - 编译器只会静默生成y不变的代码.显然,这是C++早期的常见错误来源.

如果我设计了C++,我的偏好是允许非const引用绑定到temporaries,但禁止在绑定到引用时从左值到临时值的自动转换.但谁知道,这可能会开辟出一种不同的蠕虫......


Che*_*Alf 6

  • "以这种方式禁止非const引用的C++标准是什么?"

相反惯例的实际经验,这是事情最初的运作方式.C++在很大程度上是一种进化的语言,而不是一种设计的语言.很大程度上,那些仍然存在的规则是有效的(虽然1998年的标准化发生了一些大的例外,例如臭名昭​​着export的委员会发明而不是标准化现有的做法).

对于绑定规则,不仅具有C++的经验,而且还具有与Fortran等其他语言类似的经验.

正如@j_random_hacker在他的回答中指出的那样(正如我写的那样得分为0,表明SO中的得分确实不能作为衡量质量的标准),最严重的问题与隐式转换和重载解决有关.

  • "我怎么能强迫GCC允许这个代码呢?"

你不能.

代替 ...

bootrec_reset(File(path, size, off), blksize);
Run Code Online (Sandbox Code Playgroud)

......写...

File f(path, size, off);
bootrec_reset(f, blksize);
Run Code Online (Sandbox Code Playgroud)

或者定义适当的重载bootrec_reset.或者,如果"聪明"代码有吸引力,您原则上可以编写,您可以在bootrec_reset(tempref(File(path, size, off)), blksize);其中简单地定义tempref以适当地返回其参数引用.但即使这是一种技术解决方案,也不要.

  • "即将推出的C++ 0x标准是否会改变这种情况,或者新标准是否会让我更适合这一点,例如所有关于右值引用的乱码?"

不,没有任何改变给定代码的东西.

但是,如果您愿意重写,那么您可以使用例如C++ 0x右值引用或上面显示的C++ 98变通方法.

干杯&hth.,


fre*_*low 6

即将推出的C++ 0x标准是否会改变这一点,或者新标准是否在这里提供了更合适的内容,例如所有关于rvalue引用的乱码?

是.由于每个名称都是左值,因此将任何表达式视为左值几乎是微不足道的:

template <typename T>
T& as_lvalue(T&& x)
{
    return x;
}

// ...

bootrec_reset(as_lvalue(File(path, size, off)), blksize);
Run Code Online (Sandbox Code Playgroud)