我在理解何时以及是否调用移动构造函数或移动赋值运算符时遇到了一些问题,特别是在具有常量数据成员的类的上下文中。考虑班级
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();
}
那么编译的结果会有所不同,具体取决于为 class 中的 move 构造函数和 move 赋值运算符提供的定义A<>
,但也取决于编译器。
1在类中没有任何进一步声明A<>
(如上),g++ (4.7.0)和 icpc (13.0.1)都可以正常编译(使用 option -std=c++11
)并产生预期的输出
ctor: P=0x7fffffffd480
test: P=0x7fffffffd480
2如果我声明
A&A::operator=(A&&) = delete;
A&A::operator=(const A&) = delete;
(鉴于必须初始化初始化列表的常量数据成员,这似乎是明智的),但不提供任何进一步的 ctor,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&)
),结果完全相同,特别是这些显式移动和复制 ctor 不会生成任何输出。
那么哪个运算符用于=
in main()
?(为什么最后一次测试没有输出?)
A<>
既然有一个常量数据成员(如果我用 替换成员,结果是相同的const*T const P;
),为什么这里完全允许这个操作const T&R
?
最后,在 g++ 和 icpc 的不同行为的情况下,如果有的话,哪个是正确的?