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

当前回答

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

其他回答

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

任何直接绑定到临时的引用都将延长其寿命[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参数的函数中得到) 引用),它会修改一个即将死亡的对象,所以没有人 无论如何都可以得到修改后的值。他说,这是最 很可能,是一个错误。

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

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

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

我想分享一个场景,我希望我能做到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描述的例子。欢迎大家的批评和建议!

谢谢。

主要的问题是

g(getx()); //error

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

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

有效,因为有时需要重用表达式的结果,而且很明显您正在处理一个临时对象。

然而,这是不可能的

X& x = getx(); // error

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

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

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

就像在c++中一样,你有一个解决这个规则的方法,但是你必须显式地告诉编译器你知道你在做什么:

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

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

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
}

——克里斯