为什么不允许获取临时对象的非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().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描述的例子。欢迎大家的批评和建议!
谢谢。
似乎关于为什么不允许这样做的最初问题已经得到了明确的回答:“因为这很可能是一个错误”。
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
}
——克里斯