为什么不允许获取临时对象的非const引用, 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

当前回答

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

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

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

如果你想这样做,你必须先创建一个对象的本地副本或将其绑定到一个const引用:

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

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

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

右值引用背后的思想是,因为这些对象无论如何都会死亡,你可以利用这一知识并实现所谓的“移动语义”,一种特定的优化:

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

其他回答

这篇关于右值引用的Visual c++博客文章:

... c++不希望你意外地 修改临时对象,但是要直接修改 在上调用非const成员函数 可修改的右值是显式的,所以 这是允许的……

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.

如果我是你,我会重新考虑我的功能设计。为什么g()接受引用,它修改参数了吗?如果不是,使它const引用,如果是,为什么你试图传递临时给它,你不在乎它是一个临时你正在修改?为什么getx()返回临时的?如果你和我们分享你的真实情况以及你想要实现的目标,你可能会得到一些关于如何做到这一点的好建议。

违背语言和愚弄编译器很少能解决问题——通常它会制造问题。


Edit: Addressing questions in comment: 1) `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.

我清楚地说明了目的:不允许修改临时对象,因为这没有意义(忽略c++ 0x右值引用)。“那么为什么允许我调用非const成员?”这个问题很好,但我没有比上面已经提到的更好的答案了。 好吧,如果我对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.

我想分享一个场景,我希望我能做到Alexey要求的事情。在一个Maya c++插件中,我必须做以下的恶作剧,以获得一个值到一个节点属性:

MFnDoubleArrayData myArrayData;
MObject myArrayObj = myArrayData.create(myArray);   
MPlug myPlug = myNode.findPlug(attributeName);
myPlug.setValue(myArrayObj);

这写起来很乏味,所以我写了以下helper函数:

MPlug operator | (MFnDependencyNode& node, MObject& attribute){
    MStatus status;
    MPlug returnValue = node.findPlug(attribute, &status);
    return returnValue;
}

void operator << (MPlug& plug, MDoubleArray& doubleArray){
    MStatus status;
    MFnDoubleArrayData doubleArrayData;
    MObject doubleArrayObject = doubleArrayData.create(doubleArray, &status);
    status = plug.setValue(doubleArrayObject);
}

现在我可以把文章开头的代码写成:

(myNode | attributeName) << myArray;

问题是它不能在Visual c++之外编译,因为它试图将从|操作符返回的临时变量绑定到<<操作符的MPlug引用。我想它是一个参考,因为这段代码被多次调用,我宁愿没有MPlug被复制这么多。我只需要临时对象存活到第二个函数结束。

这是我的情形。我只是想展示一个人们想做Alexey描述的例子。欢迎大家的批评和建议!

谢谢。

为什么要在c++常见问题中讨论(粗体部分):

在c++中,非const引用可以绑定到左值,const引用可以绑定到左值或右值,但是没有任何东西可以绑定到非const右值。这是为了防止人们更改临时对象的值,因为临时对象的新值在使用之前就被销毁了。例如:

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

如果允许这个incr(0),要么是一些没人见过的临时值被增加,要么更糟——0的值将变成1。后者听起来很愚蠢,但实际上在早期的Fortran编译器中有一个类似的错误,即留出一个内存位置来保存值0。

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

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

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

如果你想这样做,你必须先创建一个对象的本地副本或将其绑定到一个const引用:

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

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

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

右值引用背后的思想是,因为这些对象无论如何都会死亡,你可以利用这一知识并实现所谓的“移动语义”,一种特定的优化:

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

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

在你的例子中,getX()不会返回一个const X,所以你可以像调用X().ref()一样调用ref()。你正在返回一个非const引用,因此可以调用非const方法,你不能做的是将ref赋值给一个非const引用。

加上SadSidos的评论,这使得你的三点不正确。