You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于可变参数继承的泛型可扩展变量类:构造时消除冗余nullptr参数的实现问题

基于可变参数继承的泛型可扩展变量类:构造时消除冗余nullptr参数的实现问题

嗨,你的这个可扩展变量类设计思路真的很棒!确实,每次构造都要传一堆nullptr太冗余了,问题的根源在于你当前的Variable构造函数要求参数包Args的长度必须和扩展类Extentions的数量完全匹配——因为你在初始化列表里直接展开Extentions(args)...,当你只传初始值时,Args是空包,和Extentions的数量不匹配,自然就会报参数包长度不匹配的错误。

下面给你两种实用的解决方案,帮你彻底去掉这些冗余的nullptr:

方案一:给扩展类添加无参构造(最直接简单)

这是最容易落地的方式,只需要给每个扩展类(比如Forceable、Immutable、ChangeCheckable等)添加一个无参构造函数,同时保留原来接受nullptr_t的构造以兼容旧代码:

template<typename T>
class Forceable : virtual public GenericVar<T>
{
public:
    // 添加无参构造,初始化默认状态
    Forceable() : forced(false) {}
    // 保留原有构造,兼容之前的调用方式
    Forceable(std::nullptr_t) : forced(false) {}
    // ... 其他成员函数
};

然后修改Variable的构造函数初始化列表,让扩展类的构造可以接受空参数(即自动调用无参构造):

template<typename Type, typename... Extentions>
template<typename... Args>
Variable<Type,Extentions...>::Variable(Type initValue, Args&&... args) 
    : GenericVar<Type>(), 
      Extentions(std::forward<Args>(args))... // 完美转发参数,无参数时调用无参构造
{
    this->value = initValue;
}

这样修改后,你就可以完全去掉那些冗余的nullptr了:

int main(int argc, char *argv[])
{
    Variable<float, Forceable<float>> test2(1.2f); // 直接传初始值,Forceable用无参构造
    Constant<float> constant(4.578f); // Constant的两个扩展类都用无参构造
    Variable<quint32, Forceable<quint32>, ChangeCheckable<quint32>> value(45); // 同理
}

对于需要传递参数的扩展类(比如你提到的Boundable、RiseDefault),直接在初始值后面跟上对应扩展类的构造参数即可,参数顺序要和扩展类的顺序一一对应:

Variable<int, Forceable<int>, Boundable<int,int>, ChangeCheckable<int>, Validable<int>, RiseDefault<int, bool, Riseable> >
value3(0, {}, {-30,50}, {}, {}, 40, new Variable<bool, Riseable>(false));

这里{}表示调用对应扩展类的无参构造,{-30,50}传递给Boundable的构造,40, new Variable<...>传递给RiseDefault的构造(确保参数类型和扩展类构造函数匹配即可)。

方案二:不修改扩展类,用SFINAE自动适配构造参数(更灵活但复杂)

如果你不想给扩展类添加无参构造,可以利用C++17的SFINAE和折叠表达式,让Variable的构造函数自动判断每个扩展类是否可以接受无参构造,或者需要传递参数。

首先,我们需要一个辅助模板来判断类是否有默认构造:

template<typename T, typename = void>
struct has_default_constructor : std::false_type {};

template<typename T>
struct has_default_constructor<T, std::void_t<decltype(T{})>> : std::true_type {};

然后,修改Variable的构造函数,使用折叠表达式来分别初始化每个扩展类:

template<typename Type, typename... Extentions>
class Variable : virtual public GenericVar<Type>, public Extentions...
{
    static_assert((std::is_base_of_v<GenericVar<Type>, Extentions> && ...), "Only virtual base of GenericVar<T> allowed as variadic.");
public:
    // 无扩展类参数的情况:所有扩展类用默认构造(要求扩展类必须支持)
    explicit Variable(Type initValue) 
        : GenericVar<Type>(), 
          Extentions()... // 调用每个扩展类的默认构造
    {
        this->value = initValue;
    }

    // 带扩展类参数的情况:参数顺序和扩展类一一对应
    template<typename... Args, 
             std::enable_if_t<sizeof...(Args) == sizeof...(Extentions), int> = 0>
    Variable(Type initValue, Args&&... args) 
        : GenericVar<Type>(), 
          Extentions(std::forward<Args>(args))...
    {
        this->value = initValue;
    }

    virtual ~Variable();
};

不过这个方案要求每个扩展类必须有默认构造,否则当你不传参数时会报错,本质和方案一类似,只是把无参构造的调用显式分开了。如果有些扩展类确实没有默认构造,你还是需要给它们传递对应的参数。

总结

推荐优先用方案一,它简单直观,只需要给每个扩展类添加一行无参构造代码,就能彻底解决冗余nullptr的问题,同时兼容原来的调用方式。如果你的扩展类有特殊构造需求,再考虑方案二的灵活适配方式。

备注:内容来源于stack exchange,提问作者Slane

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.22 08:50:29