Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

move ctor of class with a constant data member or a reference member

I have some problems understanding when and if the move constructor or move assignment operator are invoked, in particular in the context of a class with constant data member. Consider the class

template<typename T> class A {
  const*T const P ;   // constant data member
  explicit A(const*T p) : P(p) { std::cerr<<" ctor: P="<<P<<'\n'; }
  void test() const { std::cerr" test: P="<<P<<'\n'; }
  // move and copy constructors and assignment operators here
};

and the test program

class B {
  int X[100];
  A<B> get_a() const { return A<B>(this); }
};

int main() {
  B b;
  A<B> a = b.get_a();   // which operator/ctor is used for '=' here?
  a.test();
}

then the results for compilation are different depending on the definitions provided for the move constructor and move assignment operator in class A<>, but also on compiler.

1 without any further declaration in class A<> (as above), both g++ (4.7.0) and icpc (13.0.1) compile fine (with option -std=c++11) and produce the expected output

ctor: P=0x7fffffffd480
test: P=0x7fffffffd480

2 if I declare

A&A::operator=(A&&) = delete;
A&A::operator=(const A&) = delete;

(which seems sensible in view of the constant data member which must be initialiser-list initialised), but don't provide any further ctor, compilation fails with g++ but is okay wich icpc. If in addition I define either (or both) of

A::A(A&&) = default;
A::A(const A&) = default;

both compilers are happy. However, g++ is not happy with the combination

A::A(A&&) = delete;
A::A(const A&) = default;

while icpc is happy.

3 If I play the same game as in 2, except that A::A(A&&) = default; is replaced by

A::A(A&&a) : P(a.P) { std::cerr<<" move ctor: P="<<P<<'\n'; } // never called?

(and equivalent for A::A(const A&)), the results are completely identical, in particular no output is generated from these explicit move and copy ctors.

So which operator is used for = in main()? (and why is no output produced in the last test?)

And why is this operation allowed here at all, given that A<> has a constant data member (the results are identical if I replace the member const*T const P; with const T&R)?

Finally, in case of the different behaviours of g++ and icpc, which, if any, is correct?

like image 584
Walter Avatar asked Nov 26 '12 13:11

Walter


1 Answers

A<B> a = b.get_a();

is not an assignment, but an initialization of a from an rvalue. This syntax should fail under C++0x if

  1. the move constructor is deleted,
  2. the move constructor is declared explicit,
  3. the copy constructor is deleted and no move constructor is defined,
  4. no move constructor is defined and, at the same time the move-assignment is defined or deleted.

Declaration or deletion of the copy assignment operator should not have any influence.

Correction: Different to the copy constructor (which is synthesized even if a user-defined copy assignment operator is provided), the compiler does not synthesize a move constructor if a user-defined move assignment is defined. Hence, the above list should be amended by 4 (which I have done now).

Hence, in my opinion,

  • in [1] in the question, both compilers behave correctly,
  • in [2]/1, gcc behaves correctly (move assignment prevents generation of move constructor), icpc is wrong,
  • in [2]/2, both compilers are correct,
  • in [2]/3, gcc is correct, since the move constructor is explicitly deleted; icpc is wrong,
  • [3] is a mystery to me. Are you sure you're correct?
like image 73
JohnB Avatar answered Sep 23 '22 04:09

JohnB