How can I make this variadic template code shorter using features from C++14 and C++1z?
Asked Answered
A

4

10

This is a code snippet that I am going to use in order to check whether the variadic template types are unique:

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

Is there any way to make this code shorter and/or more concise using features introduced in C++14 and C++1z? Or is there a better way to achieve the same effect using the new features?

In the case of C++1z I mean: features that are already available in the newest versions of Clang and GCC.

Al answered 1/12, 2015 at 16:35 Comment(4)
No, that's pretty concise as is. When fold expressions are introduced, though, you'll be able to do something like: constexpr static bool value = std::is_same<F, T>::value || ...Winnifredwinning
@BrianRodriguez: That needs round parentheses I think.Ehrlich
You can use a little trickery to make the is_one_of a little more concise: coliru.stacked-crooked.com/a/3b9755f28193a13bShowbread
@PiotrSkotnicki yes, exactly. Does it use any new features (that were not present in C++11 or not developed to that stage) other than fold expressions?Al
C
6
#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...>;

DEMO

Carrizales answered 1/12, 2015 at 17:31 Comment(0)
F
10

We recently added std::disjunction to the C++1z draft, which can be used for is_one_of (and it stops instantiating as soon as it finds a match, see the link for more details):

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

This is already implemented in GCC trunk. For older versions of GCC you can use the implementation detail __or_ instead:

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

Or implement disjunction by hand using C++11 facilities, as shown at the end of the proposal linked to above.

Flita answered 1/12, 2015 at 17:48 Comment(0)
C
6
#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...>;

DEMO

Carrizales answered 1/12, 2015 at 17:31 Comment(0)
W
3

I'd (now) suggest using the std::conj/disj/nega family of STL functions:

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

When fold-expressions, which were designed for these cases, are released into the language this will become trivial:

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 {};
Winnifredwinning answered 1/12, 2015 at 17:4 Comment(4)
Your is_unique only checks if H is unique, but other Ts could still have duplicates in the type list.Showbread
This code is broken in so many different ways that it's clear that it has never been even minimally tested.Conundrum
@Conundrum I'll change it when I wake up tomorrow. I only just recently gave myself access to these new features, and forgot to fix this while having so much fun and etcWinnifredwinning
@Conundrum Done. All that's keeping it from compiling now is the constexpr-ness of std::any_ofWinnifredwinning
D
2

I'm in line with Brian Rodriguez's and Piotr Scontnincki's answers, as far as it concerns the fold expressions part. Until folding expressions are in, you could shrink the existing code a little bit by getting rid of the incomplete primary templates as follows:

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;
};
Dunlop answered 1/12, 2015 at 17:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.