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

南楼画角 提交于 2019-12-23 09:04:14

问题


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?


回答1:


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?


来源:https://stackoverflow.com/questions/13565594/move-ctor-of-class-with-a-constant-data-member-or-a-reference-member

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!