Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

gcc and clang both elide the call to the move constructor in the snippet below. Is this correct?

In the code below an object s of class S is used to initialize an object of class D with a direct-initialization D d(s);. The conversion function S::operator D() is used to convert the object s into a temporary object of type D. Then, gcc and clang both elide the explicit call to the move constructor D(&&), to move this temporary object into d. See live example.

#include <iostream>
struct D;
struct S{ operator D(); };

struct D{
    D(){}
    D(D&&) { std::cout << "move constructor" << '\n'; }
};

S::operator D() { std::cout << "conversion function" << '\n'; return D(); }

int main()
{
    S s;
    D d(s);
}

I'm disputing the correctness of this elision, on the following grounds:

  1. This case is covered in the first sub-bullet point in §8.5/16 (N3337), which is silent about elision.

    If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. The applicable constructors are enumerated (13.3.1.3), and the best one is chosen through overload resolution (13.3). The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s). If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed.

  2. Note that the next sub-bullet point explicitly mentions the possibility of elision.
  3. The call to the move constructor is explicit. How can it be elided?
like image 728
Belloc Avatar asked Dec 02 '14 12:12

Belloc


1 Answers

The C++ standard loves to create exceptions to rules defined in one place in a completely different place.

The copy/move elision rules are specified in 12.8/31. There are two copy/move operations to be eliminated in your code.

The first is easy: within operator D, the temporary constructed in the return expression is moved to the temporary that represents the function return value. Bullet 3 permits elision of this move.

The second is the move of the temporary function return value to the d object. Again, bullet 3 permits the elision.

  • when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move
like image 174
Sebastian Redl Avatar answered Sep 20 '22 08:09

Sebastian Redl