c++ - CRTP 派生类在使用继承的赋值运算符后丢失其成员和段错误?

标签 c++ templates inheritance crtp

我正在尝试使用 4 1/2 移动语义规则并使用 CRTP 从流程中删除重复项。事实证明这很困难,因为尽管进行了编译,但当我在使用赋值运算符后尝试访问派生类的成员时,以下代码最终会出现段错误。为什么会发生这种情况,有没有办法解决这个问题?

基本 CRTP 类

// 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? 
    }
};

派生类

// 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);使用父类,因为它没有编译,而且调试器似乎表明成员被维护了。我曾尝试将其切换回去,但没有成功,该函数现在显示为:

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)

这个赋值运算符切片 rhs到类型 BaseCRTP<Derived>当原始参数的类型为 Derived 时.这就是成员“丢失”的地方。

您可以让接线员接受 BaseCRTP &&而不是 using它在 Derived , 实现 operator=那里:

Derived& Derived::operator= (Derived rhs)
{
  return BaseCRTP::operator= (std::move(rhs));
}

关于c++ - CRTP 派生类在使用继承的赋值运算符后丢失其成员和段错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44591523/

相关文章:

c++ - 从模板继承时需要合格的访问权限

带有引用参数的 C++ 模板隐式实例化

java - 将异常消息传递给父类,而不使用 throw new exception(); 构造​​ , 消息;

java - 我可以从子级的重载方法调用祖先的方法吗?

c++ - 什么是 C++ 模块,它们与 namespace 有何不同?

c++ - 结构对象析构函数

C++:查找可分为两组的数组项的所有组合

c++ - std::call_once 抛出 std::system_error(未知错误 -1)

c++ - 如何从迭代器获取迭代器底层类型?

类层次结构中的 Java 可选接口(interface)