使用常量数据成员或引用成员移动类的ctor [英] move ctor of class with a constant data member or a reference member
问题描述
我有一些问题理解何时和如果移动构造函数或移动赋值运算符被调用,特别是在具有常量数据成员的类的上下文中。
考虑类
template< typename T> class A {
const * T const P; //常数数据成员
显式A(const * T p):P(p){std :: cerr ' }
void test()const {std :: cerrtest:P =<<<<'\\\
'; }
//移动和复制构造函数和赋值运算符
};
和测试程序
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 for for'='here?
a.test();
}
那么编译结果是不同的,具体取决于为move构造函数提供的定义并且在 A<>
中移动赋值运算符,也可以在编译器上移动赋值运算符。
strong>没有在 A<>
(如上)中的任何进一步的声明,g ++(4.7.0)和icpc(13.0.1) code> -std = c ++ 11 )并产生预期输出
ctor :P = 0x7fffffffd480
test:P = 0x7fffffffd480
2 如果我声明
A& A :: operator =(A&&
A& A :: operator =(const A&)= delete;
(鉴于必须初始化初始化列表的常量数据成员,这似乎是明智的)不提供任何进一步的ctor,编译失败与g ++,但是好的wich icpc。如果另外我定义
A :: A(A&
A :: A(const A&)= default;
两个编译器都很开心。但是,g ++不满意组合
A :: A(A&&
A :: A(const A&)= default;
而icpc很高兴。
strong> 3 如果我使用与 2 中相同的游戏,除了 A :: A(A&&)=默认;
替换为
A :: A(A& a):P(aP){std :: cerr< ;<move ctor:P =<< P<<'\\\
'; } //从未调用过?
(等效于 A :: A(const A& code>),结果完全相同,特别是从这些显式移动和复制转换器生成无输出。
该操作符用于 main()
中的 =
? (为什么在最后一个测试中没有产生输出)
为什么这里允许这个操作,假设 A&
有一个常量数据成员(如果我替换成员 const * T const P;
与 const T& ; R
)?
最后,如果g ++和icpc的行为不同, >
A< B& a = b.get_a();
不是赋值,而是 a
从右值。如果
- 移动构造函数已删除,则此语法将在C ++ 0x下失败,
- move constructor被声明为
explicit
, - 复制构造函数被删除,并且没有定义move constructor
- 未定义移动构造函数,同时定义或删除移动任务。
声明或删除
更正:与复制构造函数(即使提供了用户定义的复制赋值运算符也是合成的)不同的是,复制赋值操作符不会有任何影响。如果定义了用户定义的移动分配,编译器不会合成移动构造函数。因此,上面的列表应该修改为4(我现在做的)。
因此,在我看来,
$ b $在问题[1]中的两个编译器行为正确,
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?
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
- the move constructor is deleted,
- the move constructor is declared
explicit
, - the copy constructor is deleted and no move constructor is defined,
- 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?
这篇关于使用常量数据成员或引用成员移动类的ctor的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!