为什么非常量引用不能绑定到临时对象?
为什么不允许获取对临时对象的非常量引用, getx() 返回哪个函数?显然,这是 C++ 标准所禁止的 但我对这种限制的目的感兴趣,不是对标准的引用。
struct X
{
X& ref() { return *this; }
};
X getx() { return X();}
void g(X & x) {}
int f()
{
const X& x = getx(); // OK
X& x = getx(); // error
X& x = getx().ref(); // OK
g(getx()); //error
g(getx().ref()); //OK
return 0;
}
- 很明显,对象的生命周期不可能是原因,因为 C++ 标准不禁止对对象的常量引用。
- 很明显,上面示例中的临时对象不是常量,因为允许调用非常量函数。例如,
ref()
可以修改临时对象。 - 此外,
ref()
允许您欺骗编译器并获取到该临时对象的链接,这解决了我们的问题。
此外:
他们说“将临时对象分配给 const 引用可以延长该对象的生命周期”和“但没有提及非 const 引用”。 我的附加问题。以下赋值是否会延长临时对象的生命周期?
X& x = getx().ref(); // OK
Why is it not allowed to get non-const reference to a temporary object,
which function getx()
returns? Clearly, this is prohibited by C++ Standard
but I am interested in the purpose of such restriction, not a reference to the standard.
struct X
{
X& ref() { return *this; }
};
X getx() { return X();}
void g(X & x) {}
int f()
{
const X& x = getx(); // OK
X& x = getx(); // error
X& x = getx().ref(); // OK
g(getx()); //error
g(getx().ref()); //OK
return 0;
}
- It is clear that the lifetime of the object cannot be the cause, because
constant reference to an object is not prohibited by C++ Standard. - It is clear that the temporary object is not constant in the sample above, because calls to non-constant functions are permitted. For instance,
ref()
could modify the temporary object. - In addition,
ref()
allows you to fool the compiler and get a link to this temporary object and that solves our problem.
In addition:
They say "assigning a temporary object to the const reference extends the lifetime of this object" and " Nothing is said about non-const references though".
My additional question. Does following assignment extend the lifetime of temporary object?
X& x = getx().ref(); // OK
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
“很明显,临时对象在上面的示例中不是恒定的,因为调用
允许非常数函数。例如,ref() 可以修改临时的
在您的示例中,
getX() 不会返回 const X,因此您可以以与调用 X().ref() 大致相同的方式调用 ref()。您将返回一个非 const ref,因此可以调用非 const 方法,但您不能将 ref 分配给非 const 引用,
再加上 SadSidos 注释,这使您的三点不正确。
"It is clear that the temporary object is not constant in the sample above, because calls
to non-constant functions are permitted. For instance, ref() could modify the temporary
object."
In your example getX() does not return a const X so you are able to call ref() in much the same way as you could call X().ref(). You are returning a non const ref and so can call non const methods, what you can't do is assign the ref to a non const reference.
Along with SadSidos comment this makes your three points incorrect.
我想分享一个场景,我希望我能做阿列克谢所要求的事情。在 Maya C++ 插件中,我必须执行以下操作才能将值放入节点属性中:
这写起来很乏味,所以我编写了以下辅助函数:
现在我可以从头开始编写代码 。
问题是它不能在 Visual C++ 之外编译,因为它试图绑定从 | 返回的临时变量 运算符到 << 的 MPlug 引用运营商。我希望它能作为参考,因为这段代码被调用了很多次,而且我不希望 MPlug 被复制这么多。我只需要临时对象一直存在到第二个函数结束为止。
嗯,这就是我的场景。只是想我会展示一个例子,人们想做阿列克谢所描述的事情。我欢迎所有的批评和建议!
谢谢。
I have a scenario I would like to share where I wish I could do what Alexey is asking. In a Maya C++ plugin, I have to do the following shenanigan in order to get a value into a node attribute:
This is tedious to write, so I wrote the following helper functions:
And now I can write the code from the beginning of the post as:
The problem is it doesn't compile outside of Visual C++, because it's trying to bind the temporary variable returned from the | operator to the MPlug reference of the << operator. I would like it to be a reference because this code is called many times and I'd rather not have MPlug being copied so much. I only need the temporary object to live until the end of the second function.
Well, this is my scenario. Just thought I'd show an example where one would like to do what Alexey describe. I welcome all critiques and suggestions!
Thanks.
来自这篇关于右值引用的 Visual C++ 博客文章:
基本上,您不应该尝试修改临时对象,因为它们是临时对象,并且随时都会消失。允许您调用非常量方法的原因是,只要您知道自己在做什么并且明确地了解它(例如使用reinterpret_cast),我们就欢迎您做一些“愚蠢”的事情。但是,如果您将临时引用绑定到非常量引用,则可以“永远”继续传递它,只是为了让您对对象的操作消失,因为在此过程中您完全忘记了这是一个临时引用。
如果我是你,我会重新考虑我的功能的设计。为什么 g() 接受引用,它会修改参数吗?如果不是,请将其设为 const 引用,如果是,为什么要尝试将临时值传递给它,你不关心它是你正在修改的临时值吗?为什么 getx() 返回临时值?如果您与我们分享您的真实场景以及您想要实现的目标,您可能会得到一些关于如何实现的好建议。
违背语言并欺骗编译器很少能解决问题——通常它会产生问题。
Edit: Addressing questions in comment:
X& x = getx().ref(); // OK,x什么时候会死?
- 我不知道,也不关心,因为这正是我所说的“违背语言”的意思。该语言说“临时对象在语句末尾死亡,除非它们绑定到 const 引用,在这种情况下,当引用超出范围时它们就会死亡”。应用该规则,看起来 x 在下一个语句的开头就已经死了,因为它没有绑定到 const 引用(编译器不知道 ref() 返回什么)。但这只是一个猜测。我清楚地说明了目的:不允许修改临时变量,因为它没有意义(忽略 C++0x 右值引用)。问题是“那为什么我可以称呼非常数成员呢?”是一个很好的答案,但我没有比上面已经说过的更好的答案。
好吧,如果我对
X& 中的 x 的看法是正确的x = getx().ref();
在语句末尾就死掉了,问题很明显。无论如何,根据您的问题和评论,我认为即使这些额外的答案也不会令您满意。这是最后的尝试/总结:C++ 委员会认为修改临时变量没有意义,因此,他们不允许绑定到非常量引用。我不知道,可能还涉及一些编译器实现或历史问题。然后,出现了一些具体情况,尽管困难重重,他们仍然允许通过调用非常量方法进行直接修改。但这是一个例外 - 通常不允许您修改临时变量。是的,C++ 常常就是那么奇怪。
From this Visual C++ blog article about rvalue references:
Basically, you shouldn't try to modify temporaries for the very reason that they are temporary objects and will die any moment now. The reason you are allowed to call non-const methods is that, well, you are welcome to do some "stupid" things as long as you know what you are doing and you are explicit about it (like, using reinterpret_cast). But if you bind a temporary to a non-const reference, you can keep passing it around "forever" just to have your manipulation of the object disappear, because somewhere along the way you completely forgot this was a temporary.
If I were you, I would rethink the design of my functions. Why is g() accepting reference, does it modify the parameter? If no, make it const reference, if yes, why do you try to pass temporary to it, don't you care it's a temporary you are modifying? Why is getx() returning temporary anyway? If you share with us your real scenario and what you are trying to accomplish, you may get some good suggestions on how to do it.
Going against the language and fooling the compiler rarely solves problems - usually it creates problems.
Edit: Addressing questions in comment:
X& x = getx().ref(); // OK when will x die?
- I don't know and I don't care, because this is exactly what I mean by "going against the language". The language says "temporaries die at the end of the statement, unless they are bound to const reference, in which case they die when the reference goes out of scope". Applying that rule, it seems x is already dead at the beginning of the next statement, since it's not bound to const reference (the compiler doesn't know what ref() returns). This is just a guess however.I stated the purpose clearly: you are not allowed to modify temporaries, because it just does not make sense (ignoring C++0x rvalue references). The question "then why am I allowed to call non-const members?" is a good one, but I don't have better answer than the one I already stated above.
Well, if I'm right about x in
X& x = getx().ref();
dying at the end of the statement, the problems are obvious.Anyway, based on your question and comments I don't think even these extra answers will satisfy you. Here is a final attempt/summary: The C++ committee decided it doesn't make sense to modify temporaries, therefore, they disallowed binding to non-const references. May be some compiler implementation or historic issues were also involved, I don't know. Then, some specific case emerged, and it was decided that against all odds, they will still allow direct modification through calling non-const method. But that's an exception - you are generally not allowed to modify temporaries. Yes, C++ is often that weird.
在代码中,
getx()
返回一个临时对象,即所谓的“右值”。您可以将右值复制到对象(也称为变量)中或将它们绑定到 const 引用(这将延长它们的生命周期,直到引用生命周期结束)。您不能将右值绑定到非常量引用。这是一个经过深思熟虑的设计决定,为了防止用户意外修改将在表达式末尾消失的对象:
如果你想这样做,你必须首先制作该对象的本地副本或将其绑定到 const 引用:
请注意,下一个 C++ 标准将包含右值引用。因此,您所知道的引用将被称为“左值引用”。您将被允许将右值绑定到右值引用,并且可以在“右值性”上重载函数:
右值引用背后的想法是,由于这些对象无论如何都会消亡,因此您可以利用这些知识并实现所谓的“移动语义”,某种优化:
In your code
getx()
returns a temporary object, a so-called "rvalue". You can copy rvalues into objects (aka. variables) or bind them to to const references (which will extend their life-time until the end of the reference's life). You cannot bind rvalues to non-const references.This was a deliberate design decision in order to prevent users from accidentally modifying an object that is going to die at the end of the expression:
If you want to do this, you will have to either make a local copy or of the object first or bind it to a const reference:
Note that the next C++ standard will include rvalue references. What you know as references is therefore becoming to be called "lvalue references". You will be allowed to bind rvalues to rvalue references and you can overload functions on "rvalue-ness":
The idea behind rvalue references is that, since these objects are going to die anyway, you can take advantage of that knowledge and implement what's called "move semantics", a certain kind of optimization:
您所展示的是允许运算符链接。
表达式为“getx().ref();”这会在分配给“x”之前执行完成。
请注意,getx() 不会返回引用,而是将完整形成的对象返回到本地上下文中。该对象是临时的,但它不是 const,因此允许您调用其他方法来计算值或发生其他副作用。
查看此答案的末尾以获得更好的示例
不能将临时对象绑定到引用,因为这样做会生成一个对对象的引用,该对象将在表达式末尾被销毁,从而留下一个悬空引用(这是不整洁的,标准不会不像不整洁)。
ref() 返回的值是有效的引用,但该方法不关心它返回的对象的生命周期(因为它在其上下文中无法拥有该信息)。您基本上刚刚完成了相当于:
使用对临时对象的 const 引用来执行此操作的原因是,标准将临时对象的生命周期延长到引用的生命周期,因此临时对象的生命周期延长了超过声明结束。
因此,唯一剩下的问题是,为什么标准不允许引用临时对象来将对象的生命周期延长到语句结束之后?
我相信这是因为这样做会使编译器很难获得正确的临时对象。这是为了对临时对象进行常量引用,因为它的用途有限,因此迫使您复制该对象来执行任何有用的操作,但确实提供了一些有限的功能。
想想这种情况:
延长这个临时对象的生命周期将会非常令人困惑。
而以下内容:
将为您提供易于使用和理解的代码。
如果您想修改该值,您应该将该值返回给变量。如果您试图避免从函数复制对象的成本(因为对象似乎是复制构造回来的(技术上是这样))。那么就不用担心编译器非常擅长'返回值优化'
What you are showing is that operator chaining is allowed.
The expression is 'getx().ref();' and this is executed to completion before assignment to 'x'.
Note that getx() does not return a reference but a fully formed object into the local context. The object is temporary but it is not const, thus allowing you to call other methods to compute a value or have other side effects happen.
Look at the end of this answer for a better example of this
You can not bind a temporary to a reference because doing so will generate a reference to an object that will be destroyed at the end of the expression thus leaving you with a dangling reference (which is untidy and the standard does not like untidy).
The value returned by ref() is a valid reference but the method does not pay any attention to the lifespan of the object it is returning (because it can not have that information within its context). You have basically just done the equivalent of:
The reason it is OK to do this with a const reference to a temporary object is that the standard extends the lifespan of the temporary to the lifespan of the reference so the temporary objects lifespan is extended beyond the end of the statement.
So the only remaining question is why does the standard not want to allow reference to temporaries to extend the life of the object beyond the end of the statement?
I believe it is because doing so would make the compiler very hard to get correct for temporary objects. It was done for const references to temporaries as this has limited usage and thus forced you to make a copy of the object to do anything useful but does provide some limited functionality.
Think of this situation:
Extending the lifespan of this temporary object is going to be very confusing.
While the following:
Will give you code that it is intuitive to use and understand.
If you want to modify the value you should be returning the value to a variable. If you are trying to avoid the cost of copying the obejct back from the function (as it seems that the object is copy constructed back (technically it is)). Then don't bother the compiler is very good at 'Return Value Optimization'
为什么在 C++ 中讨论常见问题解答(粗体我的):
Why is discussed in the C++ FAQ (boldfacing mine):
主要问题是这
是一个逻辑错误:
g
正在修改getx()
的结果,但您没有任何机会检查修改后的对象。如果 g 不需要修改其参数,那么它就不需要左值引用,它可以按值或常量引用获取参数。是有效的,因为您有时需要重用表达式的结果,并且很明显您正在处理一个临时对象。
然而,如果不使
g(getx())
有效,就不可能使其有效,这是语言设计者首先试图避免的。
是有效的,因为方法只知道
this
的常量性,它们不知道它们是在左值还是右值上调用。与 C++ 中一样,您有一个解决此规则的方法,但您必须明确地向编译器发出信号,表明您知道自己在做什么:
The main issue is that
is a logical error:
g
is modifying the result ofgetx()
but you don't have any chance to examine the modified object. Ifg
didn't need to modify its parameter then it wouldn't have required an lvalue reference, it could have taken the parameter by value or by const reference.is valid because you sometimes need to reuse the result of an expression, and it's pretty clear that you're dealing with a temporary object.
However it is not possible to make
valid without making
g(getx())
valid, which is what the language designers were trying to avoid in the first place.is valid because methods only know about the const-ness of the
this
, they don't know if they are called on an lvalue or on an rvalue.As always in C++, you have a workaround for this rule but you have to signal the compiler that you know what you're doing by being explicit:
似乎关于为什么不允许这样做的原始问题已经得到了明确的回答:“因为这很可能是一个错误”。
FWIW,我想我会展示如何可以完成它,尽管我认为这不是一个好的技术。
我有时想要将临时值传递给采用非常量引用的方法的原因是故意丢弃调用方法不关心的通过引用返回的值。像这样的事情:
正如之前的答案中所解释的,它无法编译。但这可以正确编译并工作(使用我的编译器):
这只是表明您可以使用强制转换来欺骗编译器。显然,声明并传递未使用的自动变量会更干净:
这种技术确实将不需要的局部变量引入了方法的范围。如果出于某种原因您想阻止它稍后在方法中使用,例如,为了避免混淆或错误,您可以将其隐藏在本地块中:
-- Chris
Seems like the original question as to why this is not allowed has been answered clearly: "because it is most likely an error".
FWIW, I thought I'd show how to it could be done, even though I don't think it's a good technique.
The reason I sometimes want to pass a temporary to a method taking a non-const reference is to intentionally throw away a value returned by-reference that the calling method doesn't care about. Something like this:
As explained in previous answers, that doesn't compile. But this compiles and works correctly (with my compiler):
This just shows that you can use casting to lie to the compiler. Obviously, it would be much cleaner to declare and pass an unused automatic variable:
This technique does introduce an unneeded local variable into the method's scope. If for some reason you want to prevent it from being used later in the method, e.g., to avoid confusion or error, you can hide it in a local block:
-- Chris
很好的问题,这是我尝试更简洁的答案(因为很多有用的信息都在注释中,很难在噪音中挖掘出来。)
任何直接绑定到临时对象的引用都会延长其寿命[12.2.5]。另一方面,用另一个引用初始化的引用将不会(即使它最终是相同的临时值)。这是有道理的(编译器不知道该引用最终指的是什么)。
但这整个想法非常令人困惑。例如,
const X &x = X();
将使临时值与x
引用一样长,但const X &x = X() .ref();
不会(谁知道 ref() 实际上返回了什么)。在后一种情况下,X
的析构函数在此行末尾被调用。 (这可以通过非平凡的析构函数观察到。)因此,这似乎通常令人困惑和危险(为什么使有关对象生命周期的规则复杂化?),但大概至少需要 const 引用,因此标准确实为他们。
所有临时变量都会持续存在,直到完整表达式结束。然而,要使用它们,您需要像
ref()
那样的技巧。这是合法的。除了提醒程序员正在发生一些不寻常的事情(即,其修改将很快丢失的引用参数)之外,似乎没有充分的理由跳过额外的环。Excellent question, and here's my attempt at a more concise answer (since a lot of useful info is in comments and hard to dig out in the noise.)
Any reference bound directly to a temporary will extend its life [12.2.5]. On the other hand, a reference initialized with another reference will not (even if it's ultimately the same temporary). That makes sense (the compiler doesn't know what that reference ultimately refers to).
But this whole idea is extremely confusing. E.g.
const X &x = X();
will make the temporary last as long as thex
reference, butconst X &x = X().ref();
will NOT (who knows whatref()
actually returned). In the latter case, the destructor forX
gets called at the end of this line. (This is observable with a non-trivial destructor.)So it seems generally confusing and dangerous (why complicate the rules about object lifetimes?), but presumably there was a need at least for const references, so the standard does set this behavior for them.
All temporaries do persist until the end of the full-expression. To make use of them, however, you need a trick like you have with
ref()
. That's legal. There doesn't seem to be a good reason for the extra hoop to jump through, except to remind the programmer that something unusual is going on (namely, a reference parameter whose modifications will be quickly lost).为什么你想要
X& x = getx();
?只需使用 X x = getx(); 并依赖 RVO。Why would you ever want
X& x = getx();
? Just useX x = getx();
and rely on RVO.邪恶的解决方法涉及“mutable”关键字。实际上是邪恶的留给读者作为练习。或者参见这里:http://www.ddj.com/cpp/184403758
The evil workaround involves the 'mutable' keyword. Actually being evil is left as an exercise for the reader. Or see here: http://www.ddj.com/cpp/184403758