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

C++使用std::conditional选择继承基类报非直接基类错误

错误根因

你在类继承列表中直接继承了std::conditional<...>模板实例本身,而非它筛选出的目标类型。
std::conditional本质是一个编译期选择工具类,真正符合你条件的目标类型是它的内嵌成员type,你之前的写法相当于让Last继承了conditional工具类,而非Derived1/Derived2,但在构造函数里你却调用了std::conditional<...>::type也就是Derived1/Derived2的构造函数,编译器检测到你构造的类型不是Last的基类,就会抛出对应报错。
模板未实例化时只会做基础语法校验,不会检查继承关系、基类构造合法性这类实例化相关逻辑,所以你在创建Last对象前不会触发编译错误。这个问题和菱形继承、虚继承没有任何关联,不要被相似的报错信息误导。

修复方案

最简洁的写法是提前给选中的基类定义别名,避免重复书写冗长的conditional判断,同时修正继承的目标类型:

#include <iostream>
#include <type_traits>

template< class >
class Base;

template< class T, class ...Args >
class Base < T(Args...) >
{
public:
    Base(int a) { std::cout << "Base constructor " << a << std::endl; }
};

template< class >
class Derived1;

template< class ...Args >
class Derived1 < void(Args...) > : public Base< void(Args...) >
{
public:
    Derived1(int a) : Base< void(Args...) >(a)
        { std::cout << "Derived1 constructor " << a << std::endl; }
};

template< class >
class Derived2;

template< class T, class ...Args >
class Derived2 < T(Args...) > : public Base< T(Args...) >
{
public:
    Derived2(int a) : Base< T(Args...) >(a)
        { std::cout << "Derived2 constructor " << a << std::endl; }
};

// 提前定义编译期选中的基类别名
template<class T, class... Args>
using LastBaseT = typename std::conditional<
    std::is_void<T>::value,
    Derived1<void(Args...)>,
    Derived2<T(Args...)>
>::type;

template< class >
class Last;

template< class T, class ...Args >
class Last< T(Args...) > : public LastBaseT<T, Args...>
{
public:
    Last(int a) : LastBaseT<T, Args...>(a)
    { 
        std::cout << "Last constructor " << a << std::endl; 
    }
};

int main()
{
    Last<int(int)> last(1);
    return 0;
}

修复后实例化Last<int(int)>会按预期依次调用Base、Derived2、Last的构造函数,输出对应日志。

优化提示
  • 如果你使用C++14及以上标准,可以直接用标准库提供的std::conditional_t别名模板,省略手写typename ...::type的步骤,写法更简洁:
    template<class T, class... Args>
    using LastBaseT = std::conditional_t<
        std::is_void<T>::value,
        Derived1<void(Args...)>,
        Derived2<T(Args...)>
    >;
    
  • 所有编译期选择基类的场景都建议提前抽出基类别名,既可以减少重复代码,也能避免继承列表、初始化列表两处条件判断写不一致的笔误。
  • 直接在继承列表手写typename std::conditional<...>::type也能通过编译,但重复书写相同逻辑的维护成本更高,不推荐使用。

内容的提问来源于stack exchange,提问作者pl0xxxy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 00:33:26