Как сделать вариативный is_same?

Как я могу создать шаблон класса, который возвращает, равен ли какой-либо из его вариативных типов первому типу. Я хочу иметь возможность сделать это:

is_same<T, A, B, C>::value; // true if T is one of A, B or C

И если T равен любому из этих типов, его статический элемент value будет true, иначе false. Как я могу это сделать?


person Me myself and I    schedule 10.06.2013    source источник
comment
Поскольку ваше намерение было неясным (два человека сделали одну и ту же неправильную интерпретацию), я взял на себя смелость немного перефразировать ваш вопрос.   -  person syam    schedule 11.06.2013


Ответы (6)


Используйте рекурсию шаблона:

template<typename T, typename... Rest>
struct is_any : std::false_type {};

template<typename T, typename First>
struct is_any<T, First> : std::is_same<T, First> {};

template<typename T, typename First, typename... Rest>
struct is_any<T, First, Rest...>
    : std::integral_constant<bool, std::is_same<T, First>::value || is_any<T, Rest...>::value>
{};

static_assert(is_any<int, char, double, int>::value, "error 1");   // OK
static_assert(is_any<int, char, double, short>::value, "error 2"); // error
person syam    schedule 10.06.2013

Красиво и лаконично с С++ 17:

template <class T, class... Ts>
struct is_any : std::disjunction<std::is_same<T, Ts>...> {};

И двойное:

template <class T, class... Ts>
struct are_same : std::conjunction<std::is_same<T, Ts>...> {};

Вариант, в котором используются выражения сгиба:

template <class T, class... Ts>
struct is_any : std::bool_constant<(std::is_same_v<T, Ts> || ...)> {};

template <class T, class... Ts>
struct are_same : std::bool_constant<(std::is_same_v<T, Ts> && ...)> {};
person mavam    schedule 17.09.2016
comment
это намного чище, чем проверенный ответ, я чувствую, что вместо этого он должен получить галочку, хотя вопрос имеет тег С++ 11 - person xception; 18.06.2019
comment
Первый вариант работает на C++14, а добавление disjunction заставит его компилироваться даже на C++11. - person erenon; 10.07.2019
comment
Мне нравится решение дизъюнкции/союза. Можно ли это записать как концепцию C++20? - person Silicomancer; 08.04.2021

В C++17 у вас есть еще более приятное решение, использующее переменные шаблона и выражения свертки:

template<class T, class... Rest>
inline constexpr bool are_all_same = (std::is_same_v<T, Rest> && ...);

И использование также проще, чем все остальные примеры:

are_all_same<T, A, B, C>

Никаких ::value, никаких скобок!

person Joald    schedule 14.09.2018

Что-то вроде этого. Во-первых, небольшая библиотека метапрограммирования, потому что она добавляет примерно 2 строки, чтобы сделать это в общем:

template<template<typename,typename>class checker, typename... Ts>
struct is_any_to_first : std::false_type {};

template<template<typename,typename>class checker, typename T0, typename T1, typename... Ts>
struct is_any_to_first<checker, T0, T1, Ts...> :
  std::integral_constant< bool, checker<T0, T1>::value || is_any_to_first<checker, T0, Ts...>::value>
{};

Затем двухстрочная реализация is_any_same_to_first:

template<typename... Ts>
using is_any_same_to_first = is_any_to_first< std::is_same, Ts... >;

И для полноты оригинальное is_all, которое тоже может оказаться полезным:

template<template<typename,typename>class checker, typename... Ts>
struct is_all : std::true_type {};

template<template<typename,typename>class checker, typename T0, typename T1, typename... Ts>
struct is_all<checker, T0, T1, Ts...> :
  std::integral_constant< bool, checker<T0, T1>::value && is_all<checker, T0, Ts...>::value>
{};

template<typename... Ts>
using is_all_same = is_all< std::is_same, Ts... >;

Живой пример is_all_same.

Обратите внимание, что вызов is_any_same_to_first чего-либо менее явного вызывает проблемы. 2/3 человек, которые пытались ответить на этот вопрос, включая меня, предположили, что is_same<A,B,C> истинно, если все три типа одного и того же типа!

person Yakk - Adam Nevraumont    schedule 10.06.2013
comment
Хм, я вижу, вы приняли другое решение, чем я (is_all_same вместо is_any). По правде говоря, я не решался спросить у ОП разъяснений по этому поводу. - person syam; 11.06.2013
comment
Гм... Этот код будет работать, только если все типы одинаковы. Я ищу, является ли T либо A, B или C. - person Me myself and I; 11.06.2013

Используя упрощенные функции constexpr C++14, такие вещи гораздо проще кодировать и, вероятно, намного быстрее компилировать, поэтому вы можете написать:

template <class T, class ... Candidates>
constexpr bool is_all_same() {
    bool pairs[] = {std::is_same<T,Candidates>::value...};
    for(bool p: pairs) if(!p) return false;
    return true;
}

template <class T, class ... Candidates>
constexpr bool is_any_same() {
    bool pairs[] = {std::is_same<T,Candidates>::value...};
    for(bool p: pairs) if(p) return true;
    return false;
}

Это возможно благодаря тому, что в C++14 функции constexpr могут иметь циклы for.

person enobayram    schedule 18.08.2016
comment
не is_same_v С++ 17? Возможно, придется использовать is_same‹›::value с С++ 14 - person xwl; 23.11.2018
comment
@xwl Вы правы, is_same<>{} работает и в C++14, поскольку struct is_same<> имеет перегрузку operator bool. - person enobayram; 24.11.2018

Самая общая версия, которая работает:

  • начиная с С++ 11

  • без зависимостей (не требуется #include <type_traits>)

  • только одно имя is_same работает для n-типов (is_same_all не требуется)

template <typename First, typename Second, typename ... Next>
struct is_same {

    template <typename A, typename B>
    struct is_same_min {
        enum { value = false };
    };

    template <typename A>
    struct is_same_min<A,A> {
        enum { value = true };
    };

    template <typename X, typename Y>
    constexpr static bool check() {
        return is_same_min<X,Y>::value;
    };

    template <typename X, typename Y, typename Z, typename ... K>
    constexpr static bool check() {
        return is_same_min<X,Y>::value and check<Y, Z, K...>();
    };

    enum { value = check<First, Second, Next...>() };
};

Просто используйте is_same<T1,T2,T3...>::value

person user1823890    schedule 10.07.2019