使用继承赋值运算符后,CRTP 派生 class 丢失其成员和段错误?
CRTP derived class loses its members and segfaults after using inherited assignment operator?
我正在尝试使用 4 1/2 移动语义规则并使用 CRTP 从进程中删除重复项。事实证明这很困难,因为尽管进行了编译,但当我在使用赋值运算符后尝试访问派生 class 的成员时,以下代码最终会出现段错误。为什么会发生这种情况,有没有办法解决这个问题?
基本 CRTP Class
// BaseCRTP.h
template<class Derived>
class BaseCRTP {
public:
BaseCRTP() {};
BaseCRTP(const BaseCRTP &rhs) {
static_cast<Derived *>(this)->setCore(static_cast<const Derived&>(rhs));
};
BaseCRTP(BaseCRTP &&rhs) {
static_cast<Derived *>(this)->swap(rhs);
}
Derived &operator=(BaseCRTP rhs){
static_cast<Derived *>(this)->swap(rhs);
Derived& d = *static_cast<Derived*>(this); // debugger shows d now has the correct value for m_member, no issue here
return *static_cast<Derived*>(this); // something happens here that causes issue?
}
};
派生Class
// Derived.h
#include "Base.h"
#include <algorithm>
class Derived : public BaseCRTP<Derived>{
private:
int m_member1;
public:
using BaseCRTP<Derived>::BaseCRTP;
using BaseCRTP<Derived>::operator=;
Derived(int member);
void setCore(const Derived& rhs);
void swap(BaseCRTP<Derived> & rhs);
int getMember() const;
};
// Derived.cpp
#include "Derived.h"
void Derived::setCore(const Derived &rhs) {
m_member1 = rhs.m_member1;
}
void Derived::swap(BaseCRTP<Derived> &rhs) {
Derived& rhs_p = static_cast<Derived&>(rhs);
std::swap(m_member1, rhs_p.m_member1); // members have correct values in debugger
}
Derived::Derived(int member) {
m_member1 = member;
}
int Derived::getMember() const{
return m_member1;
}
主要
// main.cpp
#include <iostream>
#include "Derived.h"
int main() {
Derived d(1);
int z = d.getMember(); // works fine
Derived dd(34);
int w = dd.getMember(); // works fine
d = dd; // after this d and dd no longer have m_member1 values
int y = dd.getMember(); //segmentation fault
int x = d.getMember(); // when swapped this also segmentation faults
std::cout << z << w << y << x << std::endl;
return 0;
}
更新:
我最初将 void swap(BaseCRTP<Derived> & rhs);
更改为使用父 class,因为它没有编译,而且调试器似乎表明成员已维护。我试图将其切换回去,但没有成功,该函数现在显示为:
void Derived::swap(Derived &rhs) {
std::swap(m_member1, rhs_p.m_member1);
}
Derived &operator=(BaseCRTP rhs)
现在是:Derived &operator=(Derived rhs)
。
这会导致以下编译时错误:
PATH\main.cpp: In function 'int main()':
PATH\main.cpp:9:9: error: ambiguous overload for 'operator=' (operand types are 'Derived' and 'Derived')
d = dd;
^~
In file included from PATH\Derived.h:7:0,
from PATH\main.cpp:2:
PATH\Base.h:14:7: note: candidate: constexpr BaseCRTP<Derived>& BaseCRTP<Derived>::operator=(const BaseCRTP<Derived>&) <deleted>
class BaseCRTP {
^~~~~~~~
PATH\Base.h:28:14: note: candidate: Derived& BaseCRTP<Derived>::operator=(Derived) [with Derived = Derived]
Derived &operator=(Derived rhs){
^~~~~~~~
In file included from PATH\main.cpp:2:0:
PATH\Derived.h:10:7: note: candidate: Derived& Derived::operator=(const Derived&) <deleted>
class Derived : public BaseCRTP<Derived>{
^~~~~~~
显然 deleted members still get to participate in overload resolution ... 至少可以说有点烦人。肯定有办法解决这个问题吗?很明显,唯一有效的运算符是 operator=
我定义为唯一未删除的运算符。
更新 2
果然,如果我同时更改签名 和 阻止它成为赋值运算符,这整个事情就会起作用。如果我改为使用以下函数:
Derived& assignmentOperator(Derived rhs){
static_cast<Derived *>(this)->swap(rhs);
Derived& d = *static_cast<Derived*>(this); // debugger shows d now has the correct value for m_member, no issue here
return *static_cast<Derived*>(this); // something happens here that causes issue?
}
并在 main.cpp
旁边做:
d.assignmentOperator(dd);
一切正常,没有编译错误,请参阅所选答案以了解为什么我的原始段错误。我将发布一个新问题来弄清楚如何解决这些讨厌的语义...
Derived &operator=(BaseCRTP rhs)
当原始参数的类型为 Derived
时,此赋值运算符将 rhs
切片为类型 BaseCRTP<Derived>
。那就是成员所在的地方 "lost."
您可以让操作员在 Derived
中接受 BaseCRTP &&
而不是 using
,在那里实施 operator=
:
Derived& Derived::operator= (Derived rhs)
{
return BaseCRTP::operator= (std::move(rhs));
}
我正在尝试使用 4 1/2 移动语义规则并使用 CRTP 从进程中删除重复项。事实证明这很困难,因为尽管进行了编译,但当我在使用赋值运算符后尝试访问派生 class 的成员时,以下代码最终会出现段错误。为什么会发生这种情况,有没有办法解决这个问题?
基本 CRTP Class
// BaseCRTP.h
template<class Derived>
class BaseCRTP {
public:
BaseCRTP() {};
BaseCRTP(const BaseCRTP &rhs) {
static_cast<Derived *>(this)->setCore(static_cast<const Derived&>(rhs));
};
BaseCRTP(BaseCRTP &&rhs) {
static_cast<Derived *>(this)->swap(rhs);
}
Derived &operator=(BaseCRTP rhs){
static_cast<Derived *>(this)->swap(rhs);
Derived& d = *static_cast<Derived*>(this); // debugger shows d now has the correct value for m_member, no issue here
return *static_cast<Derived*>(this); // something happens here that causes issue?
}
};
派生Class
// Derived.h
#include "Base.h"
#include <algorithm>
class Derived : public BaseCRTP<Derived>{
private:
int m_member1;
public:
using BaseCRTP<Derived>::BaseCRTP;
using BaseCRTP<Derived>::operator=;
Derived(int member);
void setCore(const Derived& rhs);
void swap(BaseCRTP<Derived> & rhs);
int getMember() const;
};
// Derived.cpp
#include "Derived.h"
void Derived::setCore(const Derived &rhs) {
m_member1 = rhs.m_member1;
}
void Derived::swap(BaseCRTP<Derived> &rhs) {
Derived& rhs_p = static_cast<Derived&>(rhs);
std::swap(m_member1, rhs_p.m_member1); // members have correct values in debugger
}
Derived::Derived(int member) {
m_member1 = member;
}
int Derived::getMember() const{
return m_member1;
}
主要
// main.cpp
#include <iostream>
#include "Derived.h"
int main() {
Derived d(1);
int z = d.getMember(); // works fine
Derived dd(34);
int w = dd.getMember(); // works fine
d = dd; // after this d and dd no longer have m_member1 values
int y = dd.getMember(); //segmentation fault
int x = d.getMember(); // when swapped this also segmentation faults
std::cout << z << w << y << x << std::endl;
return 0;
}
更新:
我最初将 void swap(BaseCRTP<Derived> & rhs);
更改为使用父 class,因为它没有编译,而且调试器似乎表明成员已维护。我试图将其切换回去,但没有成功,该函数现在显示为:
void Derived::swap(Derived &rhs) {
std::swap(m_member1, rhs_p.m_member1);
}
Derived &operator=(BaseCRTP rhs)
现在是:Derived &operator=(Derived rhs)
。
这会导致以下编译时错误:
PATH\main.cpp: In function 'int main()':
PATH\main.cpp:9:9: error: ambiguous overload for 'operator=' (operand types are 'Derived' and 'Derived')
d = dd;
^~
In file included from PATH\Derived.h:7:0,
from PATH\main.cpp:2:
PATH\Base.h:14:7: note: candidate: constexpr BaseCRTP<Derived>& BaseCRTP<Derived>::operator=(const BaseCRTP<Derived>&) <deleted>
class BaseCRTP {
^~~~~~~~
PATH\Base.h:28:14: note: candidate: Derived& BaseCRTP<Derived>::operator=(Derived) [with Derived = Derived]
Derived &operator=(Derived rhs){
^~~~~~~~
In file included from PATH\main.cpp:2:0:
PATH\Derived.h:10:7: note: candidate: Derived& Derived::operator=(const Derived&) <deleted>
class Derived : public BaseCRTP<Derived>{
^~~~~~~
显然 deleted members still get to participate in overload resolution ... 至少可以说有点烦人。肯定有办法解决这个问题吗?很明显,唯一有效的运算符是 operator=
我定义为唯一未删除的运算符。
更新 2
果然,如果我同时更改签名 和 阻止它成为赋值运算符,这整个事情就会起作用。如果我改为使用以下函数:
Derived& assignmentOperator(Derived rhs){
static_cast<Derived *>(this)->swap(rhs);
Derived& d = *static_cast<Derived*>(this); // debugger shows d now has the correct value for m_member, no issue here
return *static_cast<Derived*>(this); // something happens here that causes issue?
}
并在 main.cpp
旁边做:
d.assignmentOperator(dd);
一切正常,没有编译错误,请参阅所选答案以了解为什么我的原始段错误。我将发布一个新问题来弄清楚如何解决这些讨厌的语义...
Derived &operator=(BaseCRTP rhs)
当原始参数的类型为 Derived
时,此赋值运算符将 rhs
切片为类型 BaseCRTP<Derived>
。那就是成员所在的地方 "lost."
您可以让操作员在 Derived
中接受 BaseCRTP &&
而不是 using
,在那里实施 operator=
:
Derived& Derived::operator= (Derived rhs)
{
return BaseCRTP::operator= (std::move(rhs));
}