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

当前回答

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

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

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

其他回答

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

FWIW,我想我要展示如何做到这一点,即使我不认为这是一个很好的技术。

我有时想将一个临时对象传递给接受非const引用的方法的原因是有意地丢弃调用方法不关心的由引用返回的值。就像这样:

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

正如在前面的回答中所解释的,这不能编译。但这编译和工作正确(与我的编译器):

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

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

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

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

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

——克里斯

问得好,下面是我试图给出的一个更简洁的答案(因为很多有用的信息都在评论中,很难在嘈杂中挖掘出来)。

任何直接绑定到临时的引用都将延长其寿命[12.2.5]。另一方面,由另一个引用初始化的引用则不会(即使它最终是相同的临时引用)。这是有意义的(编译器不知道该引用最终指向什么)。

但这整个想法非常令人困惑。例如const X &x = X();const x &x = x ().ref();will NOT(谁知道ref()实际返回了什么)。在后一种情况下,X的析构函数在这一行的末尾被调用。(这可以通过非平凡析构函数观察到。)

因此,这通常看起来是令人困惑和危险的(为什么要把关于对象生存期的规则复杂化呢?),但至少需要const引用,所以标准确实为它们设置了这种行为。

[来自sbi comment]:请注意,将其绑定到const引用可以增强a Temporary的生命周期是一个故意添加的异常 (TTBOMK,以便允许手动优化)。没有 为非const引用添加的异常,因为绑定是临时的 对非const的引用被认为最有可能是程序员 错误。

所有临时变量都将持续到完整表达式结束。然而,要使用它们,就需要像使用ref()一样的技巧。这是合法的。除了提醒程序员正在发生一些不寻常的事情(即,一个引用形参的修改很快就会丢失)之外,似乎没有什么好的理由来跳过这个额外的箍。

[另一个sbi评论]Stroustrup给出的(在D&E中)不允许绑定的原因 对非const引用的右值是,如果Alexey的g()会修改 对象(可以从接受非const参数的函数中得到) 引用),它会修改一个即将死亡的对象,所以没有人 无论如何都可以得到修改后的值。他说,这是最 很可能,是一个错误。

我想分享一个场景,我希望我能做到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。

这篇关于右值引用的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.