如何使用模板参数包实现 SFINAE 仅限于少数类型

Posted

技术标签:

【中文标题】如何使用模板参数包实现 SFINAE 仅限于少数类型【英文标题】:How implement SFINAE limited to few types, using template parameter pack 【发布时间】:2020-05-30 00:35:17 【问题描述】:

我需要启用 std::string 和 int,但使用参数包。

template <typename... ParamType, typename = typename std::enable_if<std::is_same<ParamType..., std::string>::value || std::is_same<ParamType..., int>::value>::type>
static inline void Log(const ParamType & ... args)



但我打电话时出错

Log("hello"s, "world"s); //syntax ERROR 

想要的结果

Log(4,3,"Hello"s); //OK
Log("Hello"s); //OK
Log(5); //OK

【问题讨论】:

【参考方案1】:

这个解决方案使用了一些 C++17-ism (std::void_t)。如果需要,它们的各种实现适用于随处可见的早期 C++ 标准:

template <typename ... ParamType,
      typename = std::void_t<std::enable_if_t
                 <std::is_same_v<ParamType, std::string> ||
                  std::is_same_v<ParamType, int>>...>>
static inline void Log(const ParamType & ... args)



【讨论】:

std::void_t 简单但有效的拼写。谢谢。【参考方案2】:

你想要(is_string_or_int(param_0)) &amp;&amp; (is_string_or_int(param_1)) &amp;&amp; (is_string_or_int(param_2)) &amp;&amp; ...。这可以用 fold 表达式来写:

typename std::enable_if<
    (true && ... && (
        std::is_same<ParamType, std::string>::value ||
        std::is_same<ParamType, int>::value
    ))
>::type

这仅适用于 C++17 及更高版本。对于 C++11 友好的解决方案,您可以使用 struct 特化来迭代类型:

// true iff Testing is any of Required...
template<typename Testing, typename... Required>
struct one_match : std::false_type ;

template<typename Testing, typename First, typename... Rest>
struct one_match<Testing, First, Rest...> : std::bool_constant<std::is_same<Testing, First>::value || one_match<Testing, Rest...>::value> ;

// true iff all of TestingTypes... are in the tuple RequiredTypesTuple
template<typename RequiredTypesTuple, typename... TestingTypes>
struct all_match;

template<typename... RequiredTypes>
struct all_match<std::tuple<RequiredTypes...>> : std::true_type ;

template<typename... RequiredTypes, typename First, typename... Rest>
struct all_match<std::tuple<RequiredTypes...>, First, Rest...> : std::bool_constant<one_match<First, RequiredTypes...>::value && all_match<std::tuple<RequiredTypes...>, Rest...>::value> ;


template <typename... ParamType, typename enabler =
typename std::enable_if<
    all_match<std::tuple<std::string, int>, ParamType...>::value
>::type>
static inline void Log(const ParamType & ... args)



【讨论】:

【参考方案3】:

在c++17 中使用折叠表达式:

#include <type_traits>

template <typename... ParamType>
static inline auto Log(const ParamType&... args)
    -> std::enable_if_t<((std::is_same_v<ParamType, std::string>
                       || std::is_same_v<ParamType, int>) && ...)>


但是,如果没有替代的 Log 重载可用于不满足条件的参数,请考虑使用 static_assert 来提供信息性错误消息:

template <typename... ParamType>
static inline void Log(const ParamType&... args)

    static_assert(((std::is_same_v<ParamType, std::string>
                 || std::is_same_v<ParamType, int>) && ...)
                  , "Only ints and strings");


在c++20 中使用概念:

#include <type_traits>

template <typename T>
concept LogParam = std::is_same_v<T, std::string> || std::is_same_v<T, int>;

static inline void Log(const LogParam auto&... args)


【讨论】:

以上是关于如何使用模板参数包实现 SFINAE 仅限于少数类型的主要内容,如果未能解决你的问题,请参考以下文章

类模板构造函数中的 SFINAE

如何使用 SFINAE 为枚举类中的缺失值制作 polyfill?

如何仅混淆少数类或单个包?

C ++参数包,仅限于具有单一类型的实例?

基于模板类型指针的条件函数行为

模板函数重载和 SFINAE 实现