Как я могу сделать код этого вариативного шаблона короче, используя возможности C++14 и C++1z?

Это фрагмент кода, который я собираюсь использовать, чтобы проверить, уникальны ли типы вариативных шаблонов:

template <typename...>
struct is_one_of;

template <typename F>
struct is_one_of<F> {
    static constexpr bool value = false;
};

template <typename F, typename S, typename... T>
struct is_one_of<F, S, T...> {
    static constexpr bool value =
        std::is_same<F, S>::value || is_one_of<F, T...>::value;
};

template <typename...>
struct is_unique;

template <>
struct is_unique<> {
    static constexpr bool value = true;
};

template <typename F, typename... T>
struct is_unique<F, T...> {
    static constexpr bool value =
        is_unique<T...>::value && !is_one_of<F, T...>::value;
};

int main() {
    constexpr bool b = is_unique<bool, int, double>::value;
    constexpr bool c = is_unique<int, char, int>::value;
    static_assert(b == true && c == false, "!");
}

Есть ли способ сделать этот код короче и/или более лаконичным, используя функции, представленные в C++14 и C++1z? Или есть лучший способ добиться того же эффекта, используя новые функции?

В случае C++1z я имею в виду: функции, которые уже доступны в новейших версиях Clang и GCC.


person syntagma    schedule 01.12.2015    source источник
comment
Нет, это довольно лаконично. Однако, когда будут введены выражения сгиба, вы сможете сделать что-то вроде: constexpr static bool value = std::is_same<F, T>::value || ...   -  person Brian Rodriguez    schedule 01.12.2015
comment
@BrianRodriguez: Думаю, здесь нужны круглые скобки.   -  person Kerrek SB    schedule 01.12.2015
comment
Вы можете использовать небольшую хитрость, чтобы сделать is_one_of немного более кратким: coliru.stacked-crooked.com /a/3b9755f28193a13b   -  person melak47    schedule 01.12.2015
comment
@PiotrSkotnicki да, точно. Использует ли он какие-либо новые функции (которых не было в C++11 или не было разработано до этого уровня), кроме выражений свертки?   -  person syntagma    schedule 01.12.2015


Ответы (4)


#include <type_traits>

template <typename F, typename... Ts>
constexpr bool is_one_of = (std::is_same<F, Ts>{} || ...);

template <typename...>
constexpr bool is_unique = true;

template <typename F, typename... Ts>
constexpr bool is_unique<F, Ts...> = is_unique<Ts...> && !is_one_of<F, Ts...>;

ДЕМО

person Piotr Skotnicki    schedule 01.12.2015

Недавно мы добавили std::disjunction в C++. 1z draft, который можно использовать для is_one_of (и он прекращает создание экземпляров, как только находит совпадение, см. ссылку для более подробной информации):

template <typename F, typename... T>
  using is_one_of = std::disjunction<is_same<F, T>...>;

Это уже реализовано в транке GCC. Для более старых версий GCC вы можете вместо этого использовать детали реализации __or_:

template <typename F, typename... T>
  using is_one_of = std::__or_<is_same<F, T>...>;

Или реализуйте disjunction вручную, используя средства C++11, как показано в конце предложения, ссылка на которое приведена выше.

person Jonathan Wakely    schedule 01.12.2015

Я бы (сейчас) предложил использовать семейство std::conj/disj/nega функций STL:

#include <type_traits>

template <typename H, typename... T>
struct is_one_of : std::disjunction<std::is_same<H, T>...> {};

template <typename H, typename... T>
struct is_unique : std::conjunction<std::negation<std::is_same<H, T>>..., is_unique<T...>> {};

template <typename H>
struct is_unique<H> : std::true_type {};

int main()
{
    static_assert(is_one_of<int, char, double, int, bool>::value);
    static_assert(is_unique<int, char, double, bool>::value);
    static_assert(!is_unique<int, int, char, double, bool>::value);
}

Когда fold-expressions, которые были разработанные для этих случаев, выпускаются на языке, это станет тривиальным:

namespace stx = std::experimental;

template <typename H, typename... T>
struct is_one_of {
    static constexpr bool value = (stx::is_same_v<H, T> || ...);
};

template <typename H, typename... T>
struct is_unique {
    static constexpr bool value = (!stx::is_same_v<H, T> && ... && is_unique<T...>::value);
};

template <typename H>
struct is_unique<H> : std::true_type {};
person Brian Rodriguez    schedule 01.12.2015
comment
Ваш is_unique проверяет только уникальность H, но другие T могут иметь дубликаты в списке типов. - person melak47; 01.12.2015
comment
Этот код взломан настолько многими способами, что становится ясно, что он никогда не тестировался даже в минимальной степени. - person T.C.; 02.02.2016
comment
@Т.С. Я поменяю его, когда проснусь завтра. Я только недавно дал себе доступ к этим новым функциям и забыл исправить это, так весело проводя время и т. д. - person Brian Rodriguez; 02.02.2016
comment
@Т.С. Сделанный. Все, что удерживает его от компиляции сейчас, это constexpr-ность std::any_of. - person Brian Rodriguez; 02.02.2016

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

template <typename...>
struct is_one_of {
    static constexpr bool value = false;
};

template <typename F, typename S, typename... T>
struct is_one_of<F, S, T...> {
    static constexpr bool value =
        std::is_same<F, S>::value || is_one_of<F, T...>::value;
};

template <typename...>
struct is_unique {
    static constexpr bool value = true;
};

template <typename F, typename... T>
struct is_unique<F, T...> {
    static constexpr bool value = is_unique<T...>::value && !is_one_of<F, T...>::value;
};
person 101010    schedule 01.12.2015