How come a non-const reference cannot bind to a temporary object?

前端 未结 11 1804
长情又很酷
长情又很酷 2020-11-21 05:19

Why is it not allowed to get non-const reference to a temporary object, which function getx() returns? Clearly, this is prohibited by C++ Standard but I am in

相关标签:
11条回答
  • 2020-11-21 05:37

    Why would you ever want X& x = getx();? Just use X x = getx(); and rely on RVO.

    0 讨论(0)
  • 2020-11-21 05:37

    Excellent question, and here's my attempt at a more concise answer (since a lot of useful info is in comments and hard to dig out in the noise.)

    Any reference bound directly to a temporary will extend its life [12.2.5]. On the other hand, a reference initialized with another reference will not (even if it's ultimately the same temporary). That makes sense (the compiler doesn't know what that reference ultimately refers to).

    But this whole idea is extremely confusing. E.g. const X &x = X(); will make the temporary last as long as the x reference, but const X &x = X().ref(); will NOT (who knows what ref() actually returned). In the latter case, the destructor for X gets called at the end of this line. (This is observable with a non-trivial destructor.)

    So it seems generally confusing and dangerous (why complicate the rules about object lifetimes?), but presumably there was a need at least for const references, so the standard does set this behavior for them.

    [From sbi comment]: Note that the fact that binding it to a const reference enhances a temporary's lifetimes is an exception that's been added deliberately (TTBOMK in order to allow manual optimizations). There wasn't an exception added for non-const references, because binding a temporary to a non-const reference was seen to most likely be a programmer error.

    All temporaries do persist until the end of the full-expression. To make use of them, however, you need a trick like you have with ref(). That's legal. There doesn't seem to be a good reason for the extra hoop to jump through, except to remind the programmer that something unusual is going on (namely, a reference parameter whose modifications will be quickly lost).

    [Another sbi comment] The reason Stroustrup gives (in D&E) for disallowing the binding of rvalues to non-const references is that, if Alexey's g() would modify the object (which you'd expect from a function taking a non-const reference), it would modify an object that's going to die, so nobody could get at the modified value anyway. He says that this, most likely, is an error.

    0 讨论(0)
  • 2020-11-21 05:40

    I have a scenario I would like to share where I wish I could do what Alexey is asking. In a Maya C++ plugin, I have to do the following shenanigan in order to get a value into a node attribute:

    MFnDoubleArrayData myArrayData;
    MObject myArrayObj = myArrayData.create(myArray);   
    MPlug myPlug = myNode.findPlug(attributeName);
    myPlug.setValue(myArrayObj);
    

    This is tedious to write, so I wrote the following helper functions:

    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);
    }
    

    And now I can write the code from the beginning of the post as:

    (myNode | attributeName) << myArray;
    

    The problem is it doesn't compile outside of Visual C++, because it's trying to bind the temporary variable returned from the | operator to the MPlug reference of the << operator. I would like it to be a reference because this code is called many times and I'd rather not have MPlug being copied so much. I only need the temporary object to live until the end of the second function.

    Well, this is my scenario. Just thought I'd show an example where one would like to do what Alexey describe. I welcome all critiques and suggestions!

    Thanks.

    0 讨论(0)
  • 2020-11-21 05:41

    From this Visual C++ blog article about rvalue references:

    ... C++ doesn't want you to accidentally modify temporaries, but directly calling a non-const member function on a modifiable rvalue is explicit, so it's allowed ...

    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.

    If I were you, I would rethink the design of my functions. Why is g() accepting reference, does it modify the parameter? If no, make it const reference, if yes, why do you try to pass temporary to it, don't you care it's a temporary you are modifying? Why is getx() returning temporary anyway? If you share with us your real scenario and what you are trying to accomplish, you may get some good suggestions on how to do it.

    Going against the language and fooling the compiler rarely solves problems - usually it creates problems.


    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.

    2) I stated the purpose clearly: you are not allowed to modify temporaries, because it just does not make sense (ignoring C++0x rvalue references). The question "then why am I allowed to call non-const members?" is a good one, but I don't have better answer than the one I already stated above.

    3) Well, if I'm right about x in X& x = getx().ref(); dying at the end of the statement, the problems are obvious.

    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.

    0 讨论(0)
  • 2020-11-21 05:42

    "It is clear that the temporary object is not constant in the sample above, because calls to non-constant functions are permitted. For instance, ref() could modify the temporary object."

    In your example getX() does not return a const X so you are able to call ref() in much the same way as you could call X().ref(). You are returning a non const ref and so can call non const methods, what you can't do is assign the ref to a non const reference.

    Along with SadSidos comment this makes your three points incorrect.

    0 讨论(0)
提交回复
热议问题