【问题标题】:Is it possible to mix SFINAE and template specialisation?是否可以混合 SFINAE 和模板专业化?
【发布时间】:2020-10-13 01:55:58
【问题描述】:

这是我大致想要实现的目标:

// 声明
模板
结构 ArgsEstimate;

// 字符串的特化,SFINAE 将是矫枉过正
模板
结构 ArgsEstimate<:string args...> {
    静态常量 std::size_t 大小 = 64 + ArgsEstimate::size;
};

// 算术类型的特化
模板::value>::type* = nullptr,
         类型名... Args>
结构 ArgsEstimate {
    静态常量 std::size_t 大小 = sizeof(AirthmeticT) + ArgsEstimate::size;
};

// 指针类型的特化
模板::value>::type* = nullptr,
         类型名... Args>
结构 ArgsEstimate {
    静态常量 std::size_t 大小 = 32 + ArgsEstimate::size;
};

问题是这段代码在我已经完成enable_if 的点上给出了编译错误“模板参数在部分特化中不可推导”。结构中的static_assert 也不起作用,因为会重新定义。

我知道,我可以单独使用 SFINAE 和函数重载来做到这一点。但是,对于像 std::string 这样的情况,使用 SFINAE 是大材小用。

所以我想知道是否有混合模板专业化和 SFINAE 的干净方式。

【问题讨论】:

    标签: c++ sfinae template-specialization partial-specialization


    【解决方案1】:

    直接回答您的问题

    你可以,但你真的不能。您的案例因可变参数模板参数而变得复杂。

    // specialisation for arithmetic types
    template<class AirthmeticT, class... Args>
    struct ArgsEstimate<
        AirthmeticT,
        std::enable_if_t<std::is_arithmetic_v<AirthmeticT>>,
        Args...>
    {
        static const std::size_t size = sizeof(AirthmeticT) + ArgsEstimate<Args...>::size;
    };
    

    这行得通……有点。您只需要确保第二个参数始终为 void:

    ArgsEstimate<int, void, /* ... */> ok; // will use the integer specialization
    
    ArgsEstimate<int, int, int> wrong; // oups, will use the base template.
    

    这是不切实际的。

    C++20 概念

    概念优雅地解决了这个问题:

    // specialisation for arithmetic types
    template<class T, class... Args>
        requires  std::is_arithmetic_v<T>
    struct ArgsEstimate<T, Args...>
    {
        static const std::size_t size = sizeof(T) + ArgsEstimate<Args...>::size;
    };
    

    前置概念解决方案

    您需要做的是将您的班级分成两个班级。只为 1 个参数定义大小的一种。在这里您可以使用 SFINAE。另一个总结它们:

    template <class T, class Enable = void>
    struct ArgEstimate {};
    
    // specialisation for string, SFINAE would be overkill
    template<>
    struct ArgEstimate<std::string&>
    {
        static const std::size_t size = 64;
    };
    
    // specialisation for arithmetic types
    template<class T>
    struct ArgEstimate<T, std::enable_if_t<std::is_arithmetic_v<T>>>
    {
        static const std::size_t size = sizeof(T);
    };
    
    // specialisation for pointer types
    template <class T>
    struct ArgEstimate<T*>
    {
        static const std::size_t size = 32;
    };
    
    // the declaration
    template<class... Args> struct ArgsEstimate;
    
    template<class T>
    struct ArgsEstimate<T>
    {
        static const std::size_t size = ArgEstimate<T>::size;
    };
    
    template<class Head, class... Tail>
    struct ArgsEstimate<Head, Tail...>
    {
        static const std::size_t size = ArgEstimate<Head>::size + ArgsEstimate<Tail...>::size;
    };
    

    如果你有 C++17,你可以使用折叠表达式来简化总和:

    template<class... Args>
    struct ArgsEstimate
    {
        static const std::size_t size = (... + ArgEstimate<Args>::size);
    };
    

    还只是想指出,指针不需要 SFINAE:

    // specialisation for pointer types
    template <class T, class... Args>
    struct ArgsEstimate<T*, Args...> {
        static const std::size_t size = 32 + ArgsEstimate<Args...>::size;
    };
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-21
      • 1970-01-01
      • 1970-01-01
      • 2021-06-05
      • 1970-01-01
      • 2012-10-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多