C++ 为什么非常量引用不能绑定到临时对象?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1565600/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 20:27:06  来源:igfitidea点击:

How come a non-const reference cannot bind to a temporary object?

c++referenceconsttemporaryc++-faq

提问by Alexey Malistov

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 referenceto the standard.

为什么不允许获取临时对象的非常量引用,哪个函数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;
}
  1. It is clear that the lifetime of the object cannot be the cause, because constant reference to an object is not prohibitedby C++ Standard.
  2. 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.
  3. In addition, ref()allows you to fool the compiler and get a link to this temporary object and that solves our problem.
  1. 很明显,对象的生命周期不能成为原因,因为C++ 标准并没有禁止对对象的常量引用。
  2. 很明显,上面示例中的临时对象不是常量,因为允许调用非常量函数。例如,ref()可以修改临时对象。
  3. 此外,ref()允许您欺骗编译器并获得指向此临时对象的链接,从而解决了我们的问题。

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?

他们说“将临时对象分配给 const 引用会延长此对象的生命周期”和“尽管没有关于非常量引用的说法”。我的附加问题。以下赋值是否会延长临时对象的生命周期?

X& x = getx().ref(); // OK

采纳答案by sbk

From this Visual C++ blog article about rvalue references:

来自这篇关于右值引用的 Visual C++ 博客文章

... C++ doesn't want you to accidentally modify temporaries, but directly calling a non-const member function on a modifiable rvalue is explicit, so it's allowed ...

... C++ 不希望您意外修改临时变量,但直接在可修改的右值上调用非常量成员函数是明确的,因此允许...

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.

基本上,您不应该尝试修改临时对象,因为它们是临时对象并且现在随时都会消亡。允许您调用非常量方法的原因是,只要您知道自己在做什么并且明确说明(例如使用 reinterpret_cast),欢迎您做一些“愚蠢”的事情。但是,如果您将临时引用绑定到非常量引用,您可以“永远”继续传递它,只是为了让您对对象的操作消失,因为在此过程中您完全忘记了这是一个临时引用。

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.

如果我是你,我会重新考虑我的功能设计。为什么 g() 接受引用,它会修改参数吗?如果不是,则将其设置为 const 引用,如果是,为什么要尝试将临时传递给它,难道您不关心它是您正在修改的临时值吗?为什么 getx() 无论如何都是临时返回的?如果您与我们分享您的真实场景以及您要实现的目标,您可能会得到一些关于如何实现的好建议。

Going against the language and fooling the compiler rarely solves problems - usually it creates problems.

违背语言并愚弄编译器很少能解决问题——通常会产生问题。



编辑:在评论中解决问题:1) X& x = getx().ref(); // OK when will x die?X& x = getx().ref(); // OK when will x die?- 我不知道也不在乎,因为这正是我所说的“违背语言”的意思。该语言说“临时文件在语句结束时死亡,除非它们绑定到 const 引用,在这种情况下,它们会在引用超出范围时死亡”。应用该规则,似乎 x 在下一条语句的开头已经死了,因为它没有绑定到 const 引用(编译器不知道 ref() 返回什么)。然而这只是一个猜测。

2) 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.

2)我明确说明了目的:不允许修改临时变量,因为它没有意义(忽略 C++0x 右值引用)。问题“那为什么我可以调用非常量成员?” 是一个很好的答案,但我没有比上面已经提到的更好的答案。

3) Well, if I'm right about x in X& x = getx().ref();dying at the end of the statement, the problems are obvious.

3) 好吧,如果我对 x 在X& x = getx().ref();语句结尾处死亡的说法是正确的,那么问题就很明显了。

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.

无论如何,根据您的问题和评论,我认为即使是这些额外的答案也不会让您满意。这是最后的尝试/总结:C++ 委员会认为修改临时变量没有意义,因此,他们不允许绑定到非常量引用。可能还涉及一些编译器实现或历史问题,我不知道。然后,出现了一些具体情况,并决定排除万难,他们仍然允许通过调用非常量方法直接修改。但这是一个例外 - 通常不允许您修改临时文件。是的,C++ 通常就是那么奇怪。

回答by sbi

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.

在您的代码中getx()返回一个临时对象,即所谓的“右值”。您可以将右值复制到对象(又名变量)或将它们绑定到常量引用(这将延长它们的生命周期直到引用生命周期结束)。您不能将右值绑定到非常量引用。

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:

这是一个深思熟虑的设计决定,以防止用户意外修改将在表达式结束时死亡的对象:

g(getx()); // g() would modify an object without anyone being able to observe

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:

如果你想这样做,你必须首先制作一个本地副本或对象,或者将它绑定到一个常量引用:

X x1 = getx();
const X& x2 = getx(); // extend lifetime of temporary to lifetime of const reference

g(x1); // fine
g(x2); // can't bind a const reference to a non-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":

请注意,下一个 C++ 标准将包括右值引用。您所知道的引用因此被称为“左值引用”。您将被允许将右值绑定到右值引用,并且您可以在“rvalue-ness”上重载函数:

void g(X&);   // #1, takes an ordinary (lvalue) reference
void g(X&&);  // #2, takes an rvalue reference

X x; 
g(x);      // calls #1
g(getx()); // calls #2
g(X());    // calls #2, too

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:

右值引用背后的想法是,由于这些对象无论如何都会消亡,您可以利用这些知识并实现所谓的“移动语义”,某种优化:

class X {
  X(X&& rhs)
    : pimpl( rhs.pimpl ) // steal rhs' data...
  {
    rhs.pimpl = NULL; // ...and leave it empty, but deconstructible
  }

  data* pimpl; // you would use a smart ptr, of course
};


X x(getx()); // x will steal the rvalue's data, leaving the temporary object empty

回答by Martin York

What you are showing is that operator chaining is allowed.

您所展示的是允许操作符链接。

 X& x = getx().ref(); // OK

The expression is 'getx().ref();' and this is executed to completion before assignment to 'x'.

表达式是 'getx().ref();' 并且在分配给'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 notconst, thus allowing you to call other methods to compute a value or have other side effects happen.

请注意, getx() 不会返回引用,而是将完全形成的对象返回到本地上下文中。该对象是临时的,但不是常量,因此允许您调用其他方法来计算值或发生其他副作用。

// It would allow things like this.
getPipeline().procInstr(1).procInstr(2).procInstr(3);

// or more commonly
std::cout << getManiplator() << 5;

Look at the end of this answer for a better example of this

查看此答案的末尾以获得更好的示例

You can notbind 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:

ref() 返回的值是一个有效的引用,但该方法不关心它返回的对象的生命周期(因为它不能在其上下文中包含该信息)。你基本上只是做了相当于:

x& = const_cast<x&>(getX());

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.

可以使用对临时对象的 const 引用来执行此操作的原因是标准将临时对象的生命周期延长到引用的生命周期,因此临时对象的生命周期会延长到语句结束之后。

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.

我相信这是因为这样做会使编译器很难正确处理临时对象。它是为对临时对象的 const 引用而完成的,因为它的用途有限,因此迫使您制作对象的副本来做任何有用的事情,但确实提供了一些有限的功能。

Think of this situation:

想想这种情况:

int getI() { return 5;}
int x& = getI();

x++; // Note x is an alias to a variable. What variable are you updating.

Extending the lifespan of this temporary object is going to be very confusing.
While the following:

延长这个临时对象的寿命将是非常令人困惑的。
而以下:

int const& y = getI();

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'

如果您想修改该值,您应该将该值返回给一个变量。如果您试图避免从函数复制对象的成本(因为对象似乎是复制构造回来的(技术上是这样))。那就不要打扰编译器很擅长'返回值优化'

回答by Tony Delroy

Whyis discussed in the C++ FAQ(boldfacingmine):

为什么C++ FAQ(我的粗体)中讨论:

In C++, non-const references can bind to lvalues and const references can bind to lvalues or rvalues, but there is nothing that can bind to a non-const rvalue. That's to protect people from changing the values of temporaries that are destroyed before their new value can be used. For example:

在 C++ 中,非常量引用可以绑定到左值,常量引用可以绑定到左值或右值,但是没有任何东西可以绑定到非常量右值。那是为了保护人们在使用新值之前不会更改已销毁的临时值的值。例如:

void incr(int& a) { ++a; }
int i = 0;
incr(i);    // i becomes 1
incr(0);    // error: 0 is not an lvalue

If that incr(0) were allowed either some temporary that nobody ever saw would be incremented or - far worse - the value of 0 would become 1. The latter sounds silly, but there was actually a bug like that in early Fortran compilers that set aside a memory location to hold the value 0.

如果允许 incr(0) 一些没有人见过的临时值会增加,或者 - 更糟糕 - 0 的值将变为 1。后者听起来很傻,但实际上在早期的 Fortran 编译器中存在类似的错误设置留出一个内存位置来保存值 0。

回答by Dan Berindei

The main issue is that

主要问题是

g(getx()); //error

is a logical error: gis modifying the result of getx()but you don't have any chance to examine the modified object. If gdidn'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.

是一个逻辑错误:g正在修改结果,getx()但您没有任何机会检查修改后的对象。如果g不需要修改它的参数,那么它就不需要左值引用,它可以通过值或常量引用来获取参数。

const X& x = getx(); // OK

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

然而,这是不可能的

X& x = getx(); // error

valid without making g(getx())valid, which is what the language designers were trying to avoid in the first place.

有效而不使g(getx())有效,这是语言设计者首先试图避免的。

g(getx().ref()); //OK

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.

是有效的,因为方法只知道 的常量性this,它们不知道是在左值还是在右值上调用它们。

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:

与在 C++ 中一样,您有一个解决此规则的方法,但您必须通过明确的方式向编译器发出信号,表明您知道自己在做什么:

g(const_cast<x&>(getX()));

回答by Chris Pearson

Seems like the original question as to whythis is not allowed has been answered clearly: "because it is most likely an error".

似乎关于为什么不允许这样做的原始问题已得到明确回答:“因为这很可能是一个错误”。

FWIW, I thought I'd show howto it could be done, even though I don't think it's a good technique.

FWIW,我想我会展示如何做到这一点,尽管我认为这不是一项好技术。

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:

我有时想将临时值传递给采用非常量引用的方法的原因是有意丢弃调用方法不关心的通过引用返回的值。像这样的东西:

// Assuming: void Person::GetNameAndAddr(std::string &name, std::string &addr);
string name;
person.GetNameAndAddr(name, string()); // don't care about addr

As explained in previous answers, that doesn't compile. But this compiles and works correctly (with my compiler):

正如之前的答案中所解释的那样,这不会编译。但这可以编译并正常工作(使用我的编译器):

person.GetNameAndAddr(name,
    const_cast<string &>(static_cast<const string &>(string())));

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:

这只是表明您可以使用强制转换来欺骗编译器。显然,声明并传递一个未使用的自动变量会更清晰:

string name;
string unused;
person.GetNameAndAddr(name, unused); // don't care about addr

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:

这种技术确实将不需要的局部变量引入了方法的作用域。如果出于某种原因您想阻止它在方法的后面使用,例如,为了避免混淆或错误,您可以将它隐藏在本地块中:

string name;
{
    string unused;
    person.GetNameAndAddr(name, unused); // don't care about addr
}

-- Chris

- 克里斯

回答by DevFred

Why would you ever want X& x = getx();? Just use X x = getx();and rely on RVO.

你为什么要X& x = getx();?只需使用X x = getx();并依赖 RVO。

回答by paul

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

邪恶的解决方法涉及 'mutable' 关键字。实际上,作恶是留给读者的练习。或者看这里:http: //www.ddj.com/cpp/184403758

回答by DS.

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 directlyto 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).

任何直接绑定到临时文件的引用都会延长其生命周期 [12.2.5]。另一方面,用另一个引用初始化的引用不会(即使它最终是同一个临时的)。这是有道理的(编译器不知道该引用最终指的是什么)。

But this whole idea is extremely confusing. E.g. const X &x = X();will make the temporary last as long as the xreference, but const X &x = X().ref();will NOT (who knows what ref()actually returned). In the latter case, the destructor for Xgets called at the end of this line. (This is observable with a non-trivial destructor.)

但这整个想法非常令人困惑。例如,const X &x = X();将使临时的持续时间与x引用一样长,但const X &x = X().ref();不会(谁知道ref()实际返回的是什么)。在后一种情况下,在X此行的末尾调用for 的析构函数。(这可以通过非平凡的析构函数观察到。)

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.

因此,这似乎通常令人困惑和危险(为什么要使有关对象生命周期的规则复杂化?),但据推测至少需要 const 引用,因此标准确实为它们设置了这种行为。

[From sbicomment]: Note that the fact that binding it to a const reference enhances a temporary's lifetimes is an exception that's been added deliberately (TTBOMK in order to allow manual optimizations). There wasn't an exception added for non-const references, because binding a temporary to a non-const reference was seen to most likely be a programmer error.

[来自sbi评论]:请注意,将其绑定到 const 引用可增强临时对象的生命周期这一事实是一个特意添加的例外(TTBOMK 以允许手动优化)。没有为非常量引用添加异常,因为将临时对象绑定到非常量引用被认为很可能是程序员错误。

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).

所有临时变量都会持续到完整表达式结束。但是,要使用它们,您需要像使用ref(). 那是合法的。似乎没有一个很好的理由让额外的环跳过,除了提醒程序员一些不寻常的事情正在发生(即,一个引用参数,其修改将很快丢失)。

[Another sbicomment] The reason Stroustrup gives (in D&E) for disallowing the binding of rvalues to non-const references is that, if Alexey's g() would modify the object (which you'd expect from a function taking a non-const reference), it would modify an object that's going to die, so nobody could get at the modified value anyway. He says that this, most likely, is an error.

[另一个sbi评论] Stroustrup 给出(在 D&E 中)禁止将右值绑定到非常量引用的原因是,如果 Alexey 的 g() 会修改对象(您期望从一个函数中获取非常量引用引用),它会修改一个即将消亡的对象,因此无论如何没有人可以获得修改后的值。他说,这很可能是一个错误。

回答by Patrick

"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."

“很明显,上面示例中的临时对象不是常量,因为允许调用非常量函数。例如, ref() 可以修改临时对象。”

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.

在您的示例中,getX() 不返回 const X,因此您可以以与调用 X().ref() 大致相同的方式调用 ref()。您正在返回一个非常量引用,因此可以调用非常量方法,您不能做的是将引用分配给非常量引用。

Along with SadSidos comment this makes your three points incorrect.

与 SadSidos 评论一起,这使您的三点不正确。