使用常量数据成员或引用成员移动类的ctor

Wal*_*ter 6 c++ constructor move-semantics c++11

我有一些问题,了解何时以及是否调用移动构造函数或移动赋值运算符,特别是在具有常量数据成员的类的上下文中.考虑上课

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
};
Run Code Online (Sandbox Code Playgroud)

和测试程序

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();
}
Run Code Online (Sandbox Code Playgroud)

那么编译的结果会有所不同,具体取决于为类中的移动构造函数和移动赋值运算符提供的定义A<>,还有编译器.

1在类中没有任何进一步的声明A<>(如上所述),g ++(4.7.0)和icpc(13.0.1)编译正确(带选项-std=c++11)并产生预期的输出

ctor: P=0x7fffffffd480
test: P=0x7fffffffd480
Run Code Online (Sandbox Code Playgroud)

2如果我宣布

A&A::operator=(A&&) = delete;
A&A::operator=(const A&) = delete;
Run Code Online (Sandbox Code Playgroud)

(鉴于必须初始化初始化的常量数据成员,这似乎是明智的),但是不提供任何进一步的ctor,编译失败了g ++但是没关系icpc.如果另外我定义了(或两者)

A::A(A&&) = default;
A::A(const A&) = default;
Run Code Online (Sandbox Code Playgroud)

两个编译器都很高兴.但是,g ++对这种组合并不满意

A::A(A&&) = delete;
A::A(const A&) = default;
Run Code Online (Sandbox Code Playgroud)

而icpc很高兴.

3如果我和2中的游戏相同,除了A::A(A&&) = default;被替换为

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

(和等效的A::A(const A&)),结果完全相同,特别是没有从这些显式移动和复制ctors生成输出.

那么,哪些运算符用于=main()?(为什么在上一次测试中没有产生输出?)

为什么这里允许这个操作,因为它A<>有一个常量数据成员(如果我用这个成员替换成员const*T const P;,结果是相同的const T&R)?

最后,在g ++和icpc的不同行为的情况下,如果有的话,这是正确的吗?

Joh*_*hnB 2

A<B> a = b.get_a();
Run Code Online (Sandbox Code Playgroud)

不是赋值,而是a右值的初始化。如果出现以下情况,此语法在 C++0x 下应该会失败

  1. 移动构造函数被删除,
  2. 声明了移动构造函数explicit
  3. 复制构造函数被删除,并且没有定义移动构造函数,
  4. 没有定义移动构造函数,同时定义或删除移动赋值。

复制赋值运算符的声明或删除不应产生任何影响。

更正:与复制构造函数(即使提供了用户定义的复制赋值运算符也会合成)不同,如果定义了用户定义的移动赋值,编译器不会合成移动构造函数。因此,上面的列表应该修改4(我现在已经完成了)。

因此,在我看来,

  • 在问题的[1]中,两个编译器的行为都正确,
  • 在 [2]/1 中,gcc 行为正确(移动赋值阻止生成移动构造函数),icpc 是错误的,
  • 在[2]/2中,两个编译器都是正确的,
  • 在[2]/3中,gcc是正确的,因为移动构造函数被显式删除;icpc 是错误的,
  • [3]对我来说是个谜。你确定你是对的吗?

  • [3] 可以用复制省略来解释。“a”对象是就地构造的。 (3认同)