为什么我的 class 中的移动分配没有被调用?
Why move assignment in my class wasn't called?
考虑这段代码:
#include <iostream>
#include <vector>
#include <initializer_list>
using namespace std;
struct BigInteger
{
vector<int> arr;
BigInteger()
{
cout << "default constructor" << endl;
this->arr.push_back(0);
}
BigInteger(initializer_list<int> il)
{
cout << "initializer_list constructor" << endl;
for (const int x : il)
{
arr.push_back(x);
}
}
BigInteger(const BigInteger& obj) //copy constructor
{
cout << "copy constructor" << endl;
this->arr = obj.arr;
}
BigInteger(BigInteger&& obj) //move constructor
{
cout << "move constructor" << endl;
swap(*this, obj);
}
~BigInteger() //destructor
{
cout << "destructor" << endl;
arr.clear(); //probably because of RAII, I guess I don't have to write this
}
BigInteger& operator=(const BigInteger& rhs)
{
cout << "copy assignment" << endl;
BigInteger tmp(rhs);
this->arr = tmp.arr;
return *this;
}
BigInteger& operator=(BigInteger&& rhs) noexcept
{
cout << "move assignment" << endl;
swap(*this, rhs);
return *this;
}
};
int main()
{
BigInteger another = BigInteger({0, 1, 2});
}
因此,我正在创建一个临时对象 BigInteger({0, 1, 2})
,然后为我的 class 实例 a
执行移动分配(理论上)。所以预期的输出是:
initializer_list constructor //creating temporary object
default constructor //creating glvalue (non-temporary) object
move assignment
destructor
但输出是:
initializer_list constructor
destructor
而且我什至不明白为什么会这样。我怀疑 operator=
与初始化不一样,但我仍然不明白我的对象是如何构造的。
首先,不会调用移动赋值运算符,因为您没有分配任何东西。 Type name = ...;
是初始化,不是赋值。
此外,甚至没有移动构造,因为 BigInteger({0, 1, 2})
是与初始化对象相同类型的纯右值,因此不会具体化临时对象,而是纯右值的初始化器是用于直接初始化another
,就好像你写了BigInteger another = {0, 1, 2};
(顺便推荐你这样写;根本不需要重复类型)。
I suspect that operator= is not the same as initialization
这是正确的。赋值运算符和初始化是两个独立的东西。您在此示例中没有使用赋值运算符。
So, I'm creating a temporary object BigInteger({0, 1, 2}) and then
doing the move assignment for my class instance a (theoretically).
你似乎是指移动构造而不是移动赋值,因为你在谈论声明。
来自 C++ 14 标准(12.8 复制和移动 class 对象)
31 When certain criteria are met, an implementation is allowed to omit
the copy/move construction of a class object, even if the constructor
selected for the copy/move operation and/or the destructor for the
object have side effects. In such cases, the implementation treats the
source and target of the omitted copy/move operation as simply two
different ways of referring to the same object, and the destruction of
that object occurs at the later of the times when the two objects
would have been destroyed without the optimization.122 This elision of
copy/move operations, called copy elision, is permitted in the
following circumstances (which may be combined to eliminate multiple
copies):
(31.3) — when a temporary class object that has not been bound to a
reference (12.2) would be copied/moved to a class object with the same
cv-unqualified type, the copy/move operation can be omitted by
constructing the temporary object directly into the target of the
omitted copy/move
所以在这个声明中
BigInteger another = BigInteger({0, 1, 2});
直接构造临时对象到被省略的目标中,省略了move操作copy/move。
考虑这段代码:
#include <iostream>
#include <vector>
#include <initializer_list>
using namespace std;
struct BigInteger
{
vector<int> arr;
BigInteger()
{
cout << "default constructor" << endl;
this->arr.push_back(0);
}
BigInteger(initializer_list<int> il)
{
cout << "initializer_list constructor" << endl;
for (const int x : il)
{
arr.push_back(x);
}
}
BigInteger(const BigInteger& obj) //copy constructor
{
cout << "copy constructor" << endl;
this->arr = obj.arr;
}
BigInteger(BigInteger&& obj) //move constructor
{
cout << "move constructor" << endl;
swap(*this, obj);
}
~BigInteger() //destructor
{
cout << "destructor" << endl;
arr.clear(); //probably because of RAII, I guess I don't have to write this
}
BigInteger& operator=(const BigInteger& rhs)
{
cout << "copy assignment" << endl;
BigInteger tmp(rhs);
this->arr = tmp.arr;
return *this;
}
BigInteger& operator=(BigInteger&& rhs) noexcept
{
cout << "move assignment" << endl;
swap(*this, rhs);
return *this;
}
};
int main()
{
BigInteger another = BigInteger({0, 1, 2});
}
因此,我正在创建一个临时对象 BigInteger({0, 1, 2})
,然后为我的 class 实例 a
执行移动分配(理论上)。所以预期的输出是:
initializer_list constructor //creating temporary object
default constructor //creating glvalue (non-temporary) object
move assignment
destructor
但输出是:
initializer_list constructor
destructor
而且我什至不明白为什么会这样。我怀疑 operator=
与初始化不一样,但我仍然不明白我的对象是如何构造的。
首先,不会调用移动赋值运算符,因为您没有分配任何东西。 Type name = ...;
是初始化,不是赋值。
此外,甚至没有移动构造,因为 BigInteger({0, 1, 2})
是与初始化对象相同类型的纯右值,因此不会具体化临时对象,而是纯右值的初始化器是用于直接初始化another
,就好像你写了BigInteger another = {0, 1, 2};
(顺便推荐你这样写;根本不需要重复类型)。
I suspect that operator= is not the same as initialization
这是正确的。赋值运算符和初始化是两个独立的东西。您在此示例中没有使用赋值运算符。
So, I'm creating a temporary object BigInteger({0, 1, 2}) and then doing the move assignment for my class instance a (theoretically).
你似乎是指移动构造而不是移动赋值,因为你在谈论声明。
来自 C++ 14 标准(12.8 复制和移动 class 对象)
31 When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization.122 This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):
(31.3) — when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move
所以在这个声明中
BigInteger another = BigInteger({0, 1, 2});
直接构造临时对象到被省略的目标中,省略了move操作copy/move。