C++ SFINAE 在模板偏特化中增加额外的约束 trait

我们经常需要确定某个类型是否为某个模板的实例,写一个 trait 即可,这里我们使用了一个非常简单的偏特化:

template <typename T>
struct A {};

template <typename>
struct is_instantiation_of_A : std::false_type {};

template <typename T>
struct is_instantiation_of_A<A<T>> : std::true_type {};

然而,有时我们希望 在偏特化中 增加额外的约束,例如要求 A 的模板参数 T 须为 trivial 类型,以下 2 种写法都是错误的:

template <typename>
struct is_trivial_instantiation_of_A : std::false_type {}; // 基模板

template <typename T, std::enable_if_t<std::is_trivial_v<T>>>
struct is_trivial_instantiation_of_A<A<T>> : std::true_type {}; // 错误

template <typename T, typename = std::enable_if_t<std::is_trivial_v<T>>>
struct is_trivial_instantiation_of_A<A<T>> : std::true_type {}; // 错误

注意: 如果不是偏特化上下文,这两种写法都对!

上述第 1 种错误写法的问题很有意思:在这个偏特化中,第 2 个模板参数依赖于第 1 个模板参数 T,而由于 T 处于一个待匹配的偏特化之中,T 依赖于“到底匹配到了哪个偏特化”这个判断,而要完成这个判断,第 2 个模板参数又必须是确定的 —— 此时就形成了模板参数 1 和 2 的相互依赖,类型推断无法完成,这是一种特殊的 非推断上下文

第 2 种错误写法的问题很明显,偏特化不能具有默认模板参数,因此我们常用的“增加占位模板参数作为开关”的技巧失效了;此外也有与第 1 种一样的错误,这里的模板参数 2 也与模板参数 1 相互依赖。

正确的写法是:

template <typename, typename = void>
struct is_trivial_instantiation_of_A : std::false_type {}; // 基模板

template <typename T>
struct is_trivial_instantiation_of_A<
    A<T>,
    std::enable_if_t<std::is_trivial_v<T>> > : std::true_type {}; // 正确

在基模板中增加一个有默认值的待定模板参数,它如同一个“插槽”,根据需要可以在特化中引入任何额外的 trait;阅读标准库,可发现这种用法比较常见。

测试(通过编译即可):

int main() {
    static_assert(  is_instantiation_of_A<A<int>>::value, "");
    static_assert(  is_trivial_instantiation_of_A<A<int>>::value, "" );
    static_assert( !is_trivial_instantiation_of_A<A<std::string>>::value, "" );

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值