C++ 为什么非常量引用不能绑定到临时对象?

C++ 为什么非常量引用不能绑定到临时对象?,c++,reference,constants,temporary,c++-faq,C++,Reference,Constants,Temporary,C++ Faq,为什么不允许获取对临时对象的非常量引用, 哪个函数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

为什么不允许获取对临时对象的非常量引用, 哪个函数
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()
    允许您愚弄编译器并获得指向此临时对象的链接,从而解决了我们的问题
  • 此外:

    他们说“给常量引用分配一个临时对象可以延长这个对象的生存期”和“但是没有关于非常量引用的说明”。 我的附加问题。下列赋值是否延长了临时对象的生存期

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

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

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

    在您的示例中,getX()不返回常量X,因此您可以使用与调用X().ref()相同的方式调用ref()。您正在返回一个非常量引用,因此可以调用非常量方法,但不能将该引用分配给非常量引用

    const X& x = getx(); // OK
    
    连同SadSidos的评论,这使你的三点不正确。

    由此:

    。。。C++不希望你意外 修改临时表,但直接修改 在上调用非常量成员函数 可修改的右值是显式的,因此 这是允许的

    基本上,你不应该试图修改临时对象,因为它们是临时对象,随时都会消失。允许您调用非常量方法的原因是,欢迎您做一些“愚蠢”的事情,只要您知道自己在做什么,并且对它有明确的认识(例如,使用reinterpret_cast)。但是,如果将一个临时引用绑定到一个非常量引用,您可以“永远”传递它,只是为了让您对对象的操作消失,因为在过程中的某个地方,您完全忘记了这是一个临时引用

    如果我是你,我会重新考虑我的功能设计。为什么g()接受引用,它会修改参数吗?如果否,则将其设置为const reference,如果是,为什么要尝试将temporary传递给它,您不在乎您正在修改的是temporary吗?为什么getx()返回的是临时的?如果您与我们分享您的真实场景以及您试图实现的目标,您可能会得到一些关于如何实现的好建议

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


    编辑:在评论中回答问题: 1)
    X&X=getx().ref();//好吧,x什么时候会死?
    -我不知道也不在乎,因为这正是我所说的“违背语言”的意思。该语言说“临时变量在语句末尾死亡,除非它们绑定到常量引用,在这种情况下,当引用超出范围时,它们就会死亡”。应用该规则,似乎x在下一条语句的开头已经死了,因为它没有绑定到const reference(编译器不知道ref()返回什么)。然而,这只是一个猜测

    2) 我清楚地说明了目的:不允许修改临时值,因为它根本没有意义(忽略C++0x右值引用)。“那么为什么我可以给非康斯特成员打电话?”这个问题是个好问题,但我没有比上面提到的更好的答案

    3) 如果我在
    x&x=getx()在语句的末尾,问题是显而易见的

    无论如何,根据你的问题和评论,我认为即使这些额外的答案也不能让你满意。这是一个最后的尝试/总结:C++委员会决定修改临时性文件是没有意义的,因此,他们禁止绑定到非const引用。可能是一些编译器实现或历史问题也涉及,我不知道。然后,出现了一些特定的情况,并决定尽管有各种可能性,他们仍然允许通过调用non-const方法进行直接修改。但这是一个例外——通常不允许修改临时表。是的,C++经常是很奇怪的。p> 在代码中
    getx()
    返回一个临时对象,即所谓的“rvalue”。您可以将右值复制到对象(也称为变量)中,或者将它们绑定到常量引用(这将延长它们的生命周期,直到引用的生命周期结束)。不能将右值绑定到非常量引用

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

    g(getx()); // g() would modify an object without anyone being able to observe
    
    如果要执行此操作,必须先创建对象的本地副本或副本,或将其绑定到常量引用:

    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++标准将包括RValk引用。因此,你们所知道的引用正被称为“左值引用”。将允许您将右值绑定到右值引用,并且可以在“右值”上重载函数:

    rvalue引用背后的思想是,由于这些对象无论如何都会消亡,您可以利用这些知识实现所谓的“移动语义”,这是一种特定的优化:

    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
    

    您显示的是允许操作员链接

     X& x = getx().ref(); // OK
    
    表达式是“getx().ref();”这是在分配前完成的
    // It would allow things like this.
    getPipeline().procInstr(1).procInstr(2).procInstr(3);
    
    // or more commonly
    std::cout << getManiplator() << 5;
    
    x& = const_cast<x&>(getX());
    
    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();
    
    g(getx()); //error
    
    const X& x = getx(); // OK
    
    X& x = getx(); // error
    
    g(getx().ref()); //OK
    
    g(const_cast<x&>(getX()));
    
    // 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
    }
    
    MFnDoubleArrayData myArrayData;
    MObject myArrayObj = myArrayData.create(myArray);   
    MPlug myPlug = myNode.findPlug(attributeName);
    myPlug.setValue(myArrayObj);
    
    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;
    
    void incr(int& a) { ++a; }
    int i = 0;
    incr(i);    // i becomes 1
    incr(0);    // error: 0 is not an lvalue