如何在多重继承中条件获取多基类的模板类型
问题说明
本文并非指定问题的重复,现有如下代码:
#include <type_traits> template <typename... Bases> struct Overloads : public Bases... {}; template <typename T> struct A { using AType = T; }; template <typename T> struct B { using BType = T; }; template <typename T> struct C { using CType = T; }; template <typename OverloadsType> struct Derived : public OverloadsType { }; int main() { // 正常通过 static_assert(std::is_same_v<typename Derived<Overloads<A<int>, B<float>, C<char>>>::AType, int>); // 正常通过 static_assert(std::is_same_v<typename Derived<Overloads<A<int>, B<float>, C<char>>>::BType, float>); // 正常通过 static_assert(std::is_same_v<typename Derived<Overloads<A<int>, B<float>, C<char>>>::CType, char>); // 编译失败:Derived<Overloads<B<float>, C<char>>>未继承A类,无AType别名 static_assert(std::is_same_v<typename Derived<Overloads<B<float>, C<char>>>::AType, void>); }
需求:当Derived未继承对应基类(如A)时,让对应的类型别名(如AType)默认是void或其他指定类型,避免编译失败。
实现方案
可以借助**SFINAE(替换失败并非错误)**机制,结合std::void_t检测基类中是否存在目标类型别名,再在Derived中提供默认类型定义。
修改后的完整代码:
#include <type_traits> template <typename... Bases> struct Overloads : public Bases... {}; template <typename T> struct A { using AType = T; }; template <typename T> struct B { using BType = T; }; template <typename T> struct C { using CType = T; }; // 辅助模板:检测类型是否包含AType template <typename T, typename = void> struct HasAType : std::false_type {}; template <typename T> struct HasAType<T, std::void_t<typename T::AType>> : std::true_type {}; // 检测BType template <typename T, typename = void> struct HasBType : std::false_type {}; template <typename T> struct HasBType<T, std::void_t<typename T::BType>> : std::true_type {}; // 检测CType template <typename T, typename = void> struct HasCType : std::false_type {}; template <typename T> struct HasCType<T, std::void_t<typename T::CType>> : std::true_type {}; template <typename OverloadsType> struct Derived : public OverloadsType { // 基类存在AType则复用,否则默认void using AType = std::conditional_t<HasAType<OverloadsType>::value, typename OverloadsType::AType, void>; // 同理处理BType和CType using BType = std::conditional_t<HasBType<OverloadsType>::value, typename OverloadsType::BType, void>; using CType = std::conditional_t<HasCType<OverloadsType>::value, typename OverloadsType::CType, void>; }; int main() { static_assert(std::is_same_v<typename Derived<Overloads<A<int>, B<float>, C<char>>>::AType, int>); static_assert(std::is_same_v<typename Derived<Overloads<A<int>, B<float>, C<char>>>::BType, float>); static_assert(std::is_same_v<typename Derived<Overloads<A<int>, B<float>, C<char>>>::CType, char>); // 现在正常通过,AType为void static_assert(std::is_same_v<typename Derived<Overloads<B<float>, C<char>>>::AType, void>); static_assert(std::is_same_v<typename Derived<Overloads<B<float>, C<char>>>::BType, float>); static_assert(std::is_same_v<typename Derived<Overloads<B<float>, C<char>>>::CType, char>); // 测试仅继承A的情况,BType和CType为void static_assert(std::is_same_v<typename Derived<Overloads<A<int>>>::BType, void>); }
原理说明
- 辅助检测模板:
HasAType这类模板利用std::void_t的特性——如果typename T::AType是合法类型,就会匹配特化版本,返回std::true_type;否则匹配主模板,返回std::false_type。 - 条件类型别名:在
Derived中用std::conditional_t做分支选择,根据检测结果决定使用基类的类型别名还是默认的void。
这样无论Overloads包含哪些基类,Derived都会拥有完整的AType、BType、CType别名,不会出现编译错误。
内容的提问来源于stack exchange,提问作者frozenca
相关产品推荐
相关产品推荐

