c++ - move 具有常量数据成员或引用成员的类的构造函数

标签 c++ constructor c++11 move-semantics

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

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
};

和测试程序

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();
}

那么编译的结果是不同的,这取决于为类 A<> 中的 move 构造函数和 move 赋值运算符提供的定义。 ,还有编译器。

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

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

2如果我声明

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

(鉴于必须初始化列表初始化的常量数据成员,这似乎是明智的),但不提供任何进一步的构造函数,g++ 编译失败但 icpc 没问题。如果另外我定义了一个(或两个)

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

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

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

虽然 icpc 很高兴。

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

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

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

那么=用的是哪个运算符在 main() ? (为什么上次测试没有输出?)

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

最后,对于 g++ 和 icpc 的不同行为,如果有的话,哪个是正确的?

最佳答案

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

不是赋值,而是从右值初始化 a。此语法在 C++0x 下应该失败,如果

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

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

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

因此,在我看来,

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

关于c++ - move 具有常量数据成员或引用成员的类的构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13565594/

相关文章:

c++ - 借助 static_assert 改进诊断

c++ - 配置文件解析器只返回以前的值

c++ - std::string erase 删除最后一个字母

c++ - 如何将 FastMM 添加到 C++ Builder 项目中?

c# - 在构造函数中传递对 'this' 的引用

java - 热切初始化与延迟初始化用例

c++ - 我的二叉树类节点的问题

应用于 Point 结构的 C++ 和交换/复制

c++ - 成员如何知道它是在哪个类实例中构造的?

c++ - 我可以将每次移动引用的数组传递给 std::thread 吗?