在以下代码行中:
bootrec_reset(File(path, size, off), blksize);
用原型调用函数:
static void bootrec_reset(File &file, ssize_t blksize);
我收到此错误:
libcpfs / mkfs.cc:99:53:错误:“File&”类型的非const引用的初始化无效来自'文件'类型的右值
libcpfs / mkfs.cc:30:13:错误:传递'void bootrec_reset(File&,ssize_t)'的参数1
我知道你不能根据标准将非const引用(const &
)传递给rvalues。但是,MSVC允许您这样做(参见this question)。 This question试图解释原因,但答案没有意义,因为他正在使用文字引用,这是一个极端的案例,显然是不允许的。
在给定的示例中,很明显会发生以下事件顺序(就像在MSVC中一样):
File
的构造函数将被调用。File
和blksize
的引用将被推送到堆栈中。bootrec_reset
使用file
。bootrec_reset
返回后,临时File
将被销毁。有必要指出File
引用需要是非const的,因为它是文件的临时句柄,在其上调用非const方法。此外,我不想将File
的构造函数参数传递给bootrec_reset
来构造那里,我也没有看到任何理由在调用者中手动构造和销毁File
对象
所以我的问题是:
答案 0 :(得分:8)
是的,普通函数不能将非const引用绑定到temporaries - 但是方法可以 - 这一事实总是让我烦恼。 TTBOMK的理由是这样的(来自this comp.lang.c++.moderated thread):
假设你有:
void inc( long &x ) { ++x; }
void test() {
int y = 0;
inc( y );
std::cout << y;
}
如果您允许long &x
的{{1}}参数绑定到由inc()
制作的临时long
副本,则此代码显然不会按预期执行 - 编译器只会静默生成保持y
不变的代码。显然,这是C ++早期的常见错误来源。
如果我设计了C ++,我的偏好是允许非const引用绑定到temporaries,但是当绑定到引用时禁止从lvalues到temporaries的自动转换。但谁知道,这可能会开辟出一种不同的蠕虫......
答案 1 :(得分:6)
相反惯例的实践经验,这是事情最初的运作方式。 C ++在很大程度上是一种进化的语言,而不是一种设计的语言。很大程度上,那些仍然存在的规则是有效的(虽然1998年的标准化发生了一些例外,例如臭名昭着的export
,委员会发明了而不是标准化现有的做法。) p>
对于绑定规则,我不仅具有C ++的经验,而且还具有与Fortran等其他语言相似的经验。
正如@j_random_hacker在他的回答中指出的那样(正如我写的那样得分为0,表明SO中的得分确实不能作为衡量质量的标准),最严重的问题与隐式转换和超载有关分辨率。
你不能。
而不是......
bootrec_reset(File(path, size, off), blksize);
...写...
File f(path, size, off);
bootrec_reset(f, blksize);
或者定义bootrec_reset
的适当重载。或者,如果“聪明”代码有吸引力,您原则上可以编写bootrec_reset(tempref(File(path, size, off)), blksize);
,您只需定义tempref
以适当地返回其参数引用。但即使这是一种技术解决方案,也不要。
不,没有任何改变给定代码的东西。
但是,如果您愿意重写,那么您可以使用例如C ++ 0x右值引用,或上面显示的C ++ 98变通方法。
干杯&amp;第h。,
答案 2 :(得分:6)
即将推出的C ++ 0x标准是否会改变这一点,或者新标准是否提供了更合适的内容,例如所有关于rvalue引用的乱码?
是。由于每个名称都是左值,因此将任何表达式视为左值几乎是微不足道的:
template <typename T>
T& as_lvalue(T&& x)
{
return x;
}
// ...
bootrec_reset(as_lvalue(File(path, size, off)), blksize);
答案 3 :(得分:2)
std::vector<type>().swap(some_vector);
应该使代码合法。答案 4 :(得分:1)
请注意,调用C ++ 0x“jibberish”并不能很好地描述您的编码能力或理解该语言的愿望。
1)实际上并非如此武断。允许非const引用绑定到r值会导致极其混乱的代码。我最近提交了一个针对MSVC的错误,该错误与此相关,其中非标准行为导致符合标准的代码无法使用异常行为进行编译和/或编译。
在您的情况下,请考虑:
#include <iostream>
template<typename T>
void func(T& t)
{
int& r = t;
++r;
}
int main(void)
{
int i = 4;
long n = 5;
const int& r = n;
const int ci = 6;
const long cn = 7;
//int& r1 = ci;
//int& r2 = cn;
func(i);
//func(n);
std::cout << r << std::endl;
}
您要编译哪条注释行?您是否希望func(i)
更改其参数而func(n)
不更改?
2)你无法编译代码。您不希望拥有该代码。未来版本的MSVC可能会删除非标准扩展,并且无法编译该代码。相反,使用局部变量。您总是可以使用额外的一对大括号来控制该局部变量的生命周期,并使其在下一行代码之前被销毁,就像临时变量一样。或r值参考。
{
File ftemp(path, size, off);
bootrec_reset(ftemp, blksize);
}
3)是的,您可以在此方案中使用C ++ 0x r值引用。
答案 5 :(得分:1)
或者,只是过载。
static void bootrec_reset(File &&file, ssize_t blksize) {
return bootrec_reset(file, blksize);
}
这是最简单的解决方案。
答案 6 :(得分:0)
如何强制GCC允许此代码?
如果您拥有File的定义,那么您可以尝试玩这样的技巧:
class File /* ... */ {
public:
File* operator&() { return this; }
/* ... */
};
/* ... */
bootrec_reset(*&File(path, size, off), blksize);
这在c ++ 98模式下为我编译。
即将推出的C ++ 0x标准是否会改变这一点,或者新标准是否提供了更合适的内容,例如所有关于rvalue引用的乱码?
显然,如果可能的话,这是一条路。