c++ 多级CRTP和初始化

68bkxrlz  于 2023-05-30  发布在  其他
关注(0)|答案(2)|浏览(143)
#include <iostream>
#include <string>
#include <memory>

struct Val
{
    std::string str;
    void append(const std::string n)
    {
        str += n;
    }
    Val() : str("x")
    {

    }
};

template<typename T>
class base
{
public:
    base(int n)
    {
        std::cout << "Base " << n << "\n";

        // triggers error if not ptr
        print();
    }

    void print()
    {
        static_cast<T*>(this)->impl_print();
    }

    ~base()
    {
        std::cout << "DB\n";
    }
};

template<typename T, typename V>
class implA : public base<implA<T, V>>
{
    friend class base<implA<T, V>>;
public:
    implA(int n) : base<implA<T, V>>(n)
    {
        std::cout << "implA " << n << "\n";

        for (int i = 0; i < n; i++)
        {
            implVal->append(std::to_string(n));
        }
    }

    ~implA()
    {
        std::cout << "iA\n";
    }

protected:
    std::shared_ptr<V> implVal =  NULL;
    void impl_print()
    {
        if (implVal == NULL)
        {
            implVal = std::make_shared<V>();
        }

        static_cast<T*>(this)->impl_print(*implVal);
    }
};

class classX : public implA<classX, Val>
{
    friend class implA<classX, Val>;
public:
    classX(int n) : implA<classX, Val>(n)
    {
        std::cout << "classX " << n << "\n";
    }

protected:
    void impl_print(Val in)
    {
        std::cout<<"p"<< in.str << "\n";
    }

};

int main()
{
    classX x(2);
    x.print();

    return 0;
}

尝试做一些多级继承,其中构造函数的参数在构造过程中修改中间层的成员。遇到的问题是,当base触发其打印时,implVal尚未分配,因此调用使用其值将导致运行时错误。
尝试通过将implVal更改为指针并在需要时手动分配来解决这个问题。这允许内部调用打印工作,但当implClass构造函数体解析时,implClass再次为null,就好像从未分配过内存一样。这会在尝试修改值时导致null ptr错误。这是怎么回事?

2q5ifsrm

2q5ifsrm1#

base(int n)
{
    std::cout << "Base " << n << "\n";

    // triggers error if not ptr
    print();
}

void print()
{
    static_cast<T*>(this)->impl_print();
}

这种模式基本上是不安全的。
直到 * 在 * 你的构造函数返回之后,你的子函数才被构造出来,并且在 * 你的子函数在C++中被构造出来之后,没有简单的方法来运行代码。
修改非构造对象(或对象的非构造部分)的成员是UB。别这么做即使它能工作,也不能保证下次编译器检测到月相变化(或其他看似无关的原因)时它能工作。
你能做的最好的就是有一个后构造模式。派生最多的类负责调用post_construct方法。
我不知道有什么方法可以干净地强制派生的T调用post-construct。

ymdaylpp

ymdaylpp2#

坚持发布的代码,你违反了这里的规则。当base构造函数运行时,implA还不存在。因此,调用implA的任何方法都是UB,因此会崩溃。
CRTP没有提供任何保护来阻止您这样做(而具有virtual函数的常规类则有很好的理由)。我使用的解决方案是有一个工厂函数,它在调用对象的方法之前构造对象。

相关问题