[英]C++ result of operator= changing after return *this
Here, I have a very simple program that moves a value from one object to another, making sure to remove the value from the one that it was taken from (leaving behind a '0').在这里,我有一个非常简单的程序,它将一个值从一个对象移动到另一个对象,确保从它所取的对象中删除该值(留下一个“0”)。
#include <iostream>
struct S
{
S(char val) : m_val(val) {}
S& operator=(S&& other) noexcept
{
this->m_val = other.m_val;
other.m_val = '0';
return *this;
}
char m_val = '0';
};
int main()
{
S a('a');
S b('b');
std::cout << "a.m_val = '" << a.m_val << "'" << std::endl;
std::cout << "b.m_val = '" << b.m_val << "'" << std::endl;
a = std::move(b);
std::cout << "a.m_val = '" << a.m_val << "'" << std::endl;
std::cout << "b.m_val = '" << b.m_val << "'" << std::endl;
return 0;
}
As expected, the output of this program is:正如预期的那样,这个程序的输出是:
a.m_val = 'a'
b.m_val = 'b'
a.m_val = 'b'
b.m_val = '0'
The value of 'b' is transferred from object b to object a, leaving a '0' behind. 'b' 的值从对象 b 转移到对象 a,留下一个 '0'。 Now, if I generalize this a bit more with a template to (hopefully) automatically do the move and delete business, here's what I end up with... (distilled down of course).
现在,如果我用一个模板(希望)自动进行移动和删除业务来概括这一点,这就是我最终得到的......(当然是精炼的)。
#include <iostream>
template<typename T>
struct P
{
P<T>& operator=(P<T>&& other) noexcept
{
T& thisDerived = static_cast<T&>(*this);
T& otherDerived = static_cast<T&>(other);
thisDerived = otherDerived;
otherDerived.m_val = '0';
return *this;
}
protected:
P<T>& operator=(const P<T>& other) = default;
};
struct S : public P<S>
{
S(char val) : m_val(val) {}
char m_val = '0';
};
int main()
{
S a('a');
S b('b');
std::cout << "a.m_val = '" << a.m_val << "'" << std::endl;
std::cout << "b.m_val = '" << b.m_val << "'" << std::endl;
a = std::move(b);
std::cout << "a.m_val = '" << a.m_val << "'" << std::endl;
std::cout << "b.m_val = '" << b.m_val << "'" << std::endl;
return 0;
}
When run, the output is:运行时,输出为:
a.m_val = 'a'
b.m_val = 'b'
a.m_val = '0'
b.m_val = '0'
Uh oh!哦哦! Somehow BOTH objects got "deleted".
不知何故,两个对象都被“删除”了。 When I step through the body of the move assignment operator code... all seems well!
当我逐步完成移动赋值运算符代码的主体时……一切似乎都很好! a.m_val is 'b' like we expect... right up until the
return *this;
a.m_val 是 'b' 就像我们期望的那样......直到
return *this;
statement.陈述。 Once it returns from the function, suddenly that value gets set back to '0'.
一旦它从函数返回,该值会突然被设置回“0”。 Can anybody please shed some light on why this is happening?
有人可以解释一下为什么会发生这种情况吗?
P<T>& operator=(P<T>&& other) noexcept
This is an explicit move assignment operator for this template class.这是此模板类的显式移动赋值运算符。
struct S : public P<S> {
This subclass inherits from this template class.这个子类继承自这个模板类。
P<S>
is its parent class. P<S>
是它的父类。
This subclass does not have an explicit move assignment operator, so your C++ compiler helpfully creates a default move assignment operator for you, because that's how C++ works.这个子类没有显式的移动赋值运算符,所以你的 C++ 编译器会帮助你创建一个默认的移动赋值运算符,因为这就是 C++ 的工作方式。 The default move-assignment operator invokes the parent class's move assignment operator, and the default move assignment operator then move-assigns all members of this class.
默认的移动赋值运算符调用父类的移动赋值运算符,然后默认的移动赋值运算符对此类的所有成员进行移动赋值。
Just because the parent class has an explicit move assignment operator (your move assignment operator) doesn't make this child class's default move assignment operator disappear.仅仅因为父类具有显式移动赋值运算符(您的移动赋值运算符)并不会使该子类的默认移动赋值运算符消失。
S
's default move assignment operator is effectively this, very loosely speaking: S
的默认移动赋值运算符实际上是这样的,非常松散地说:
S &operator=(S &&other)
{
P<S>::operator=(std::move(other));
this->m_val=std::move(other.m_val);
return *this;
}
That's what you get for free, from your C++ compiler.这就是您从 C++ 编译器免费获得的。 Isn't it nice of your C++ compiler to provide such a useful default move assignment operator for your class?
您的 C++ 编译器为您的类提供如此有用的默认移动赋值运算符不是很好吗?
a = std::move(b);
This actually ends up invoking the above default move assignment operator.这实际上最终会调用上面的默认移动赋值运算符。
Which first invokes the parent class's move assignment operator, the one you wrote.它首先调用父类的移动赋值运算符,即您编写的那个。
Which effectively sets other.m_val
to '0'
.这有效地将
other.m_val
设置为'0'
。
And when it returns, this default move assignment operator also sets this->m_val
to '0'
.当它返回时,这个默认的移动赋值运算符还将
this->m_val
为'0'
。
The problem is that S
has a implicitly generated move assignment operator , which calls the move assignment operator of the base class (ie the P<T>::operator=
), then perform member-wise move assignment on the members (ie S::m_val
).问题是
S
有一个隐式生成的移动赋值运算符,它调用基类的移动赋值运算符(即P<T>::operator=
),然后对成员执行成员移动赋值(即S::m_val
)。 In the P<T>::operator=
, other.m_val
has been assigned to '0'
, then back to S::operator=
this->m_val
is assigned by other.m_val
and becomes '0'
too.在
P<T>::operator=
, other.m_val
已经被赋值为'0'
,然后回到S::operator=
this->m_val
被other.m_val
并且也变成了'0'
。
You can define a user-defined move assignment operator for S
and does nothing expect calling the base class version.您可以为
S
定义一个用户定义的移动赋值运算符,并且除了调用基类版本之外什么也不做。 eg例如
struct S : public P<S>
{
S(char val) : m_val(val) {}
char m_val = '0';
S& operator=(S&& other) {
P<S>::operator=(other);
return *this;
}
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.