繁体   English   中英

Constexpr CRTP 析构函数

[英]Constexpr CRTP destructor

我创建了constexpr Recurring Template Pattern 的constexpr版本,所有似乎都按预期工作,除了“在正常情况下”应该标记为virtual的析构函数。 据我所知, virtualconstexpr的主要敌人。

在我的示例中,我实现了两个没有数据成员的接口。 在一般情况下(使用数据成员)让virtual ~Crtp() = default; , virtual ~FeatureNamesInterface() = default; virtual ~FeatureValuesInterface() = default; 注释掉并让编译器定义析构函数? 这种方法有内存泄漏吗? 是否有更好的方法来保护它们? 欢迎任何其他适用于 constexpr 的解决方案!

界面代码是这样的

namespace lib
{
    template <typename Derived, template<typename> class CrtpType>
    struct Crtp
    {
        //virtual ~Crtp() = default;
        [[nodiscard]] Derived& child() noexcept { return static_cast<Derived&>(*this); }
        [[nodiscard]] constexpr Derived const& child() const noexcept { return static_cast<const Derived&>(*this); }
    private:
        constexpr Crtp() = default;
        friend CrtpType<Derived>;
    };

    template<typename Derived>
    struct FeatureNamesInterface : Crtp<Derived, FeatureNamesInterface>
    {
        constexpr FeatureNamesInterface() = default;
        //virtual ~FeatureNamesInterface() = default;
        [[nodiscard]] constexpr auto& GetFeatureNames() const noexcept { return Crtp<Derived, FeatureNamesInterface>::child().GetNames(); }
    };

    template<typename Derived>
    struct FeatureDataInterface : Crtp<Derived, FeatureDataInterface>
    {
        constexpr FeatureDataInterface() = default;
        //virtual ~FeatureValuesInterface() = default;
        [[nodiscard]] constexpr auto GetFeatureData() const { return Crtp<Derived, FeatureDataInterface>::child()(); }
    };
}

两个示例类的实现如下所示

namespace impl
{
    class ChildOne final : public lib::FeatureNamesInterface<ChildOne>, public lib::FeatureDataInterface<ChildOne>
    {
        static constexpr std::array mNames{"X"sv, "Y"sv, "Z"sv};
    public:
        constexpr ChildOne() : FeatureNamesInterface(), FeatureDataInterface() {}
        ~ChildOne() = default;
        
        [[nodiscard]] constexpr auto& GetNames() const noexcept { return mNames; }
        
        [[nodiscard]] constexpr auto operator()() const noexcept
        {
            std::array<std::pair<std::string_view, double>, mNames.size()> data;
            double value = 1.0;
            for (std::size_t i = 0; const auto& name : mNames)
                data[i++] = {name, value++};

            return data;
        }
    };

    class ChildTwo final : public lib::FeatureNamesInterface<ChildTwo>, public lib::FeatureDataInterface<ChildTwo>
    {
        static constexpr std::array mNames{"A"sv, "B"sv, "C"sv, "D"sv, "E"sv, "F"sv};
    public:
        constexpr ChildTwo() : FeatureNamesInterface(), FeatureDataInterface() {}
        ~ChildTwo() = default;
        
        [[nodiscard]] constexpr auto& GetNames() const noexcept { return mNames; }

        [[nodiscard]] constexpr auto operator()() const noexcept
        {
            std::array<std::pair<std::string_view, double>, mNames.size()> data;
            double value = 4.0;
            for (std::size_t i = 0; const auto& name : mNames)
                data[i++] = {name, value++};

            return data;
        }
    };
}

完整的例子可以在这里找到。

除了“正常情况下”应该标记为虚拟的析构函数

这里有一些问题。 听起来你是在假设“所有类都应该有一个虚拟析构函数”,这是不正确的。

仅当派生类可能通过此类的指针被删除时,才需要virtual析构函数。 出于安全考虑,如果类具有任何其他虚方法,通常也鼓励始终使用虚析构函数,因为在这些情况下很可能是前者,并且由此产生的额外开销可以忽略不计,因为已经存在一个 vtable。

另一方面,如果一个类没有虚方法,那么通常没有理由在指向基类的指针中保存指向派生对象的指针。 最重要的是,虚拟析构函数的开销成比例地变得更大,因为没有它根本不需要 vtable,所以虚拟析构函数更有可能造成弊大于利。

指向基本 CRTP 类型的指针通常从不存在,因为:

  • 他们只有一个派生类。
  • 由于对派生类的强制转换,基类本身无法使用。

是否有更好的方法来保护它们?

这是一个很好的方法,尽管在这种情况下不是严格要求的。 它确保唯一调用析构函数的是派生类的析构函数。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM