13

Consider the following example:

#include <cstdio>

class object
{
public:
    object()
    {
        printf("constructor\n");
    }

    object(const object &)
    {
        printf("copy constructor\n");
    }

    object(object &&)
    {
        printf("move constructor\n");
    }
};

static object create_object()
{
    object a;
    object b;

    volatile int i = 1;

// With #if 0, object's copy constructor is called; otherwise, its move constructor.
#if 0
    if (i)
        return b; // moves because of the copy elision rules
    else
        return a; // moves because of the copy elision rules
#else
    // Seems equivalent to the above, but behaves differently.
    return i ? b : a; // copies (with g++ 4.7)
#endif
}

int main()
{
    auto data(create_object());

    return 0;
}

And consider this bit from the C++11 Working Draft, n3337.pdf, 12.8 [class.copy], point 32:

When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If overload resolution fails, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object’s type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue. [Note: This two-stage overload resolution must be performed regardless of whether copy elision will occur. It determines the constructor to be called if elision is not performed, and the selected constructor must be accessible even if the call is elided. —end note ]

Thus, if we use #if 1 in the example, the move constructor is first tried when returning the object, and then the copy constructor. Since we have a move constructor, it is used instead of the copy constructor.

In the last return statement in create_object() however, we've observed that the move constructor is not used. Is or is this not a violation of the language rules? Does the language require that the move constructor is used in the last return statement?

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
Jelle Geerts
  • 833
  • 8
  • 12

1 Answers1

13

The specification for the conditional operator is so complicated it is scary. But I believe that your compiler is correct in its behavior. See 5.16 [expr.cond]/p4:

If the second and third operands are glvalues of the same value category and have the same type, the result is of that type and value category ...

Also see 12.8 [class.copy], p31, b1 which describes when copy elision is allowed:

in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv- unqualified type as the function return type, the copy/move operation can be omitted ...

The expression is not the name of an automatic object, but is a conditional expression (and that conditional expression is an lvalue). So copy elision is not allowed here, and there is nothing else that says that the lvalue expression can pretend to be an rvalue for overload resolution.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • Could the language be improved in this regard? I feel that the `#if 0` and `#else` blocks _should_ be semantically equivalent (though they aren't, as discussed). – Jelle Geerts Aug 11 '12 at 23:48
  • @JelleGeerts: Perhaps. It is not an easy task though. The last time I got the language improved, it took me a decade to do it. That isn't to say you shouldn't try. Others have managed to do it far more quickly. And I'm quite happy that I did try. I only mean to say that you should do a cost/benefit analysis on the effort. Is using `if else` really such a great burden? The best way to start this process is to email the author of the cwg issues list: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html . – Howard Hinnant Aug 12 '12 at 03:00
  • I'll pass. Perhaps somebody can pick this up when communicating matters like this. – Jelle Geerts Aug 12 '12 at 12:36