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

当前回答

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

其他回答

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

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

表达式是'getx().ref();',在赋值给'x'之前执行完成。

注意,getx()并不返回引用,而是返回一个进入本地上下文中的完整形式的对象。对象是临时的,但它不是const,因此允许您调用其他方法来计算值或发生其他副作用。

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

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

请看这个答案的最后一个更好的例子

您不能将临时对象绑定到引用,因为这样做将生成对对象的引用,该对象将在表达式的末尾被销毁,从而留给您一个悬空引用(这是不整洁的,标准不喜欢不整洁)。

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

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

对临时对象的const引用可以这样做的原因是,标准将临时对象的生命周期延长到引用的生命周期,因此临时对象的生命周期延长到语句结束之后。

那么剩下的唯一问题是,为什么标准不允许引用临时对象来将对象的生命延长到语句结束之后呢?

我相信这是因为这样做会使编译器很难得到正确的临时对象。这样做是为了对临时对象的const引用,因为它的用途有限,因此迫使你复制对象来做任何有用的事情,但确实提供了一些有限的功能。

想想这种情况:

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

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

延长这个临时对象的生命周期会让人非常困惑。 同时:

int const& y = getI();

将为您提供易于使用和理解的代码。

如果你想修改值,你应该将值返回给一个变量。如果您试图避免从函数中复制对象的代价(因为看起来对象是复制构造回来的(技术上它是))。那就不用麻烦了编译器很擅长"返回值优化"

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

谢谢。

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

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

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
}

——克里斯

为什么要在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。