在以下代码行中:
bootrec_reset(File(path, size, off), blksize);
使用原型调用函数:
static void bootrec_reset(File &file, ssize_t blksize);
我收到此错误:
libcpfs/mkfs.cc:99:53:错误:“File&”类型的非常量引用的初始化无效来自“File”类型的右值
libcpfs/mkfs.cc:30:13: 错误:传递“void bootrec_reset(File&, ssize_t)”的参数 1
我知道您不能传递非常量引用 (< code>const &) 根据标准转换为右值。不过,MSVC 允许您执行此操作(请参阅此问题)。 这个问题试图解释原因,但答案没有任何意义,因为他使用的是对文字的引用,这是一个极端的情况,显然应该被禁止。
在给定的示例中,可以清楚地看到将发生以下事件顺序(就像在 MSVC 中一样):
- 将调用
File
的构造函数。
- 对
File
和blksize
的引用被推送到堆栈上。
-
bootrec_reset
利用file
。
- 从
bootrec_reset
返回后,临时File
被销毁。
有必要指出,File
引用必须是非常量的,因为它是文件的临时句柄,在该文件上调用非常量方法。此外,我不想将 File
的构造函数参数传递给 bootrec_reset
来在那里构造,我也没有看到任何手动构造和销毁 的理由调用者中的 File
对象。
所以我的问题是:
- C++ 标准不允许以这种方式进行非常量引用的理由是什么?
- 我如何强制 GCC 允许此代码?
- 即将到来的 C++0x 标准是否会改变这一点,或者新标准是否给了我更合适的东西,例如所有关于右值引用的胡言乱语?
In the following line of code:
bootrec_reset(File(path, size, off), blksize);
Calling a function with prototype:
static void bootrec_reset(File &file, ssize_t blksize);
I receive this error:
libcpfs/mkfs.cc:99:53: error: invalid initialization of non-const reference of type 'File&' from an rvalue of type 'File'
libcpfs/mkfs.cc:30:13: error: in passing argument 1 of 'void bootrec_reset(File&, ssize_t)'
I'm aware that you can not pass non-const references (const &
) to rvalues according to the standard. MSVC however allows you to do this (see this question). This question attempts to explain why but the answer makes no sense as he is using references to literals, which are a corner case and should obviously be disallowed.
In the given example it's clear to see that following order of events will occur (as it does in MSVC):
File
's constructor will be called.
- A reference to the
File
, and blksize
, are pushed on the stack.
bootrec_reset
makes use of file
.
- After returning from
bootrec_reset
, the temporary File
is destroyed.
It's necessary to point out that the File
reference needs to be non-const, as it's a temporary handle to a file, on which non-const methods are invoked. Furthermore I don't want to pass the File
's constructor arguments to bootrec_reset
to be constructed there, nor do I see any reason to manually construct and destroy a File
object in the caller.
So my questions are:
- What justifies the C++ standard disallowing non-const references in this manner?
- How can I force GCC to permit this code?
- Does the upcoming C++0x standard change this in anyway, or is there something the new standard gives me that is more appropriate here, for example all that jibberish about rvalue references?
发布评论
评论(7)
是的,普通函数不能将非常量引用绑定到临时对象——但方法可以——这一事实一直困扰着我。 TTBOMK 的基本原理是这样的(源自 this comp. lang.c++.moderated thread):
假设您有:
如果您允许
inc()
的long &x
参数绑定到临时y 生成的 >long 副本,这段代码显然不会执行您所期望的操作 - 编译器只会默默地生成使
y
保持不变的代码。显然,这是早期 C++ 时代常见的错误来源。如果我设计了 C++,我的偏好是允许非常量引用绑定到临时对象,但在绑定到引用时禁止从左值自动转换为临时对象。但谁知道呢,这很可能会引发另一堆蠕虫......
Yes, the fact that plain functions cannot bind non-const references to temporaries -- but methods can -- has always bugged me. TTBOMK the rationale goes something like this (sourced from this comp.lang.c++.moderated thread):
Suppose you have:
If you allowed the
long &x
parameter ofinc()
to bind to a temporarylong
copy made fromy
, this code obviously wouldn't do what you expect -- the compiler would just silently produce code that leavesy
unchanged. Apparently this was a common source of bugs in the early C++ days.Had I designed C++, my preference would have been to allow non-const references to bind to temporaries, but to forbid automatic conversions from lvalues to temporaries when binding to references. But who knows, that might well have opened up a different can of worms...
与相反惯例的实践经验,这就是事情最初的运作方式。 C++ 在很大程度上是一种进化的语言,而不是设计的语言。很大程度上,仍然存在的规则是那些被证明有效的规则(尽管 1998 年标准化中出现了一些重大例外,例如臭名昭著的
export
,该规则是委员会发明的,而不是标准化现有实践) 。对于绑定规则,不仅有C++的经验,而且对Fortran等其他语言也有类似的经验。
正如 @j_random_hacker 在他的回答中指出的那样(正如我所写的,得分为 0,这表明 SO 中的评分确实不能作为质量衡量标准),最严重的问题与隐式转换和重载解析有关。
你不能。
而不是……
写……
或者定义一个适当的
bootrec_reset
重载。或者,如果“聪明”的代码有吸引力,原则上您可以编写bootrec_reset(tempref(File(path, size, off)), blksize);
,您只需在其中定义tempref
以适当地 const 强制转换返回其参数引用。但即使这是一个技术解决方案,也不要这样做。不,对于给定的代码来说,没有任何改变。
但是,如果您愿意重写,则可以使用例如 C++0x 右值引用,或上面显示的 C++98 解决方法。
干杯&呵呵,
Practical experience with the opposite convention, which was how things worked originally. C++ is to a large degree an evolved language, not a designed one. Largely, the rules that are still there are those that turned out to work (although some BIG exceptions to that occurred with the 1998 standardization, e.g. the infamous
export
, where the committee invented rather than standardizing existing practice).For the binding rule one had not only the experience in C++, but also similar experience with other languages such as Fortran.
As @j_random_hacker notes in his answer (which as I wrote this was scored 0, showing that the scoring in SO really doesn't work as a measure of quality), the most serious problems have to do with implicit conversions and overload resolution.
You can't.
Instead of ...
... write ...
Or define an appropriate overload of
bootrec_reset
. Or, if "clever" code appeals, you can in principle writebootrec_reset(tempref(File(path, size, off)), blksize);
, where you simply definetempref
to return its argument reference appropriately const-casted. But even though that's a technical solution, don't.Nope, nothing that changes things for the given code.
If you're willing to rewrite, however, then you can use e.g. C++0x rvalue references, or the C++98 workarounds shown above.
Cheers & hth.,
是的。由于每个名称都是左值,因此将任何表达式视为左值几乎是微不足道的:
Yes. Since every name is an lvalue, it is almost trivial to treat any expression as if it was an lvalue:
std::vector< ;type>().swap(some_vector);
)File &&< /code> 应该使代码合法。
std::vector<type>().swap(some_vector);
)File &&
should make the code legal.请注意,将 C++0x 称为“胡言乱语”并不能很好地展现您的编码能力或理解该语言的愿望。
1)其实并不是那么随意。允许非常量引用绑定到右值会导致代码极其混乱。我最近针对 MSVC 提交了一个与此相关的错误,其中非标准行为导致符合标准的代码无法编译和/或以异常行为进行编译。
对于您的情况,请考虑:
您想要编译哪一行注释行?您是否希望
func(i)
更改其参数,而func(n)
不这样做?2)你不能让该代码编译。您不想拥有该代码。 MSVC 的未来版本可能会删除非标准扩展,并且无法编译该代码。相反,请使用局部变量。您始终可以使用一对额外的大括号来控制该局部变量的生命周期,并使其在下一行代码之前被销毁,就像临时变量一样。或 r 值引用。
3) 是的,在这种情况下您可以使用 C++0x 右值引用。
Please note that calling C++0x "jibberish" is not presenting a very favorable picture of your coding ability or desire to understand the language.
1) Is actually not so arbitrary. Allowing non-const references to bind to r-values leads to extremely confusing code. I recently filed a bug against MSVC which relates to this, where the non-standard behavior caused standard-compliant code to fail to compile and/or compile with a deviant behavior.
In your case, consider:
Which of the commented lines to you want to compile? Do you want
func(i)
to change its argument andfunc(n)
to NOT do so?2) You can't make that code compile. You don't want to have that code. A future version of MSVC is probably going to remove the non-standard extension and fail to compile that code. Instead, use a local variable. You can always use a extra pair of braces to control the lifetime of that local variable and cause it to be destroyed prior to the next line of code, just like the temporary would be. Or r-value references.
3) Yes, you can use C++0x r-value references in this scenario.
或者,简单地超载。
这是最简单的解决方案。
Alternatively, simply overload.
This is the easiest solution.
如果您拥有 File 的定义,那么您可以尝试玩一些技巧,例如:
这会在 c++98 模式下为我编译。
显然,如果可能的话,这是要走的路。
If you own the definition of File then you can try playing tricks such as this one:
This compiles for me in c++98 mode.
Obviously this the way to go if at all possible.