Как реализовать SFINAE, ограниченную несколькими типами, с использованием пакета параметров шаблона - PullRequest
1 голос
/ 30 мая 2020

Мне нужно включить 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

Ответы [ 3 ]

2 голосов
/ 30 мая 2020

В этом решении используется немного 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)
{

}
0 голосов
/ 30 мая 2020

В с использованием складных выражений:

#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");
}

В с использованием концепций:

#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)
{
}
0 голосов
/ 30 мая 2020

Вы хотите (is_string_or_int(param_0)) && (is_string_or_int(param_1)) && (is_string_or_int(param_2)) && .... Это можно записать с помощью выражения свертки:

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

Это работает только с C ++ 17 и выше. Для дружественного к C ++ 11 решения вы можете go для структурных специализаций для перебора типов:

// 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)
{

}
...