Check If a Type Is Passed in Variadic Template Parameter Pack

Check if a type is passed in variadic template parameter pack

You're looking for std::disjunction. It's specified in N4564 [meta.logical].

#include <type_traits>

template<typename T, typename... Ts>
constexpr bool contains()
{ return std::disjunction_v<std::is_same<T, Ts>...>; }

static_assert( contains<int, bool, char, int, long>());
static_assert( contains<bool, bool, char, int, long>());
static_assert( contains<long, bool, char, int, long>());
static_assert(not contains<unsigned, bool, char, int, long>());

Live demo


Or, adapted to a struct

template<typename T, typename... Ts>
struct contains : std::disjunction<std::is_same<T, Ts>...>
{};

Or, using fold expressions

template<typename T, typename... Ts>
struct contains : std::bool_constant<(std::is_same<T, Ts>{} || ...)>
{};

Live demo

Check if parameter pack contains a type

No, you have to use (partial) specialization with variadic templates to do compile-time computations like this:

#include <type_traits>

template < typename Tp, typename... List >
struct contains : std::true_type {};

template < typename Tp, typename Head, typename... Rest >
struct contains<Tp, Head, Rest...>
: std::conditional< std::is_same<Tp, Head>::value,
std::true_type,
contains<Tp, Rest...>
>::type {};

template < typename Tp >
struct contains<Tp> : std::false_type {};

There is only one other intrinsic operation for variadic templates and that is the special form of the sizeof operator which computes the length of the parameter list e.g.:

template < typename... Types >
struct typelist_len
{
const static size_t value = sizeof...(Types);
};

Where are you getting "it has serious compilation-time overhead" with boost mpl from? I hope you are not just making assumptions here. Boost mpl uses techniques such as lazy template instantiation to try and reduce compile-times instead of exploding like naive template meta-programming does.

Checking if variadic template parameters are unique using fold expressions

You approach doesn't really work because is_one_of needs to be called with a type T and all the remaining types not including T. There's no way of expressing that with a fold expression over a single parameter pack. I suggest using specialization instead:

template <typename...>
inline constexpr auto is_unique = std::true_type{};

template <typename T, typename... Rest>
inline constexpr auto is_unique<T, Rest...> = std::bool_constant<
(!std::is_same_v<T, Rest> && ...) && is_unique<Rest...>
>{};

Usage:

static_assert(is_unique<>);
static_assert(is_unique<int>);
static_assert(is_unique<int, float, double>);
static_assert(!is_unique<int, float, double, int>);

live example on wandbox.org


(Thanks to Barry for the simplification that uses a fold expression.)

How can you statically check that a type T exists in a variadic template parameter list

Based on François' answer, here's a shorter version that avoids usage of std::tuple and uses std::integral_constant (via true/false_type) and provides a C++14-style contains_v alias. The general idea is the same though.

template <typename T, typename... Args>
struct contains;

template <typename T>
struct contains<T> : std::false_type {};

template <typename T, typename... Args>
struct contains<T, T, Args...> : std::true_type {};

template <typename T, typename A, typename... Args>
struct contains<T, A, Args...> : contains<T, Args...> {};

template <typename T, typename... Args>
constexpr bool contains_v = contains<T, Args...>::value;

You can use it like this:

static_assert(contains_v<float, float, double>,
"failure: float not among <float, double>"); // does not trigger

static_assert(contains_v<int, float, double>,
"failure: int not among <float, double>"); // triggers

How to check the type of passed arguments to variadic function

The only way I found is to make a helper function using SFINAE

//Basic function
template<typename T>
void allsame(T) {}

//Recursive function
template<typename T, typename T2, typename... Ts,
typename = std::enable_if_t<std::is_same<T, T2>::value>>
void allsame(T arg, T2 arg2, Ts... args)
{
allsame(arg2, args...);
}

You can then call it like so:

allsame(arg...);

The compiler will then throw an error if the types are not the same.


For 2), you could modfiy allsame to return the type. The only drawback to this function is that it won't work if the type isn't default constructable.

template<typename T>
T allsame(T) { return{}; }

T allsame(T arg, T2 arg2, Ts... args)

Then, you can decltype(allsame(args...)) to get the type

How can I check type T is among parameter pack Ts...?

In your own implementation, one issue is that C++ doesn't allow partial specialization on function templates.

You can use the fold expression (which is introduced in C++17) instead of recursive function call.

template<class T1, class... Ts>
constexpr bool is_one_of() noexcept {
return (std::is_same_v<T1, Ts> || ...);
}

If you are using C++11 where fold expression and std::disjunction are not available, you can implement is_one_of like this:

template<class...> struct is_one_of: std::false_type {};
template<class T1, class T2> struct is_one_of<T1, T2>: std::is_same<T1, T2> {};
template<class T1, class T2, class... Ts> struct is_one_of<T1, T2, Ts...>: std::conditional<std::is_same<T1, T2>::value, std::is_same<T1, T2>, is_one_of<T1, Ts...>>::type {};

Specifying one type for all arguments passed to variadic function or variadic template function w/out using array, vector, structs, etc?

You can just accept the arguments by the variadic template and let typechecking check the validity later on when they are converted.

You can check convertibility on the function interface level though, to make use of overload resolution for rejecting outright wrong arguments for example, by using SFINAE

template<typename R, typename...> struct fst { typedef R type; };

template<typename ...Args>
typename fst<void,
typename enable_if<
is_convertible<Args, ToType>::value
>::type...
>::type
f(Args...);

For your use-case if you know the steps to go from an std::array<> to your dragon_list_t then you have already solved it though according to the first option above ("convert-later"):

template<typename ...Items>
dragon_list_t make_dragon_list(Items... maidens) {
std::array<Maiden, sizeof...(Items)> arr = {{ maidens ... }};
// here be dragons
}

If you combine this with the above is_convertible approach you have a reject-early template that also does overload resolution on arguments and rejects them if not applicable.

In a variadic function template can the return type be deduced from the template parameter pack elements

I want to know if there is any way that this can be achieved.

No.

In C/C++ (that are statically typed languages) the type returned from a function must depends from the types of the arguments, not from the values.

So the types returned from the following calls

find_item(1, -1, "hello", 'Z', 10.03);
find_item(2, -1, "hello", 'Z', 10.03);
find_item(3, -1, "hello", 'Z', 10.03);

must be the same.

Point.

You can get around this rule returning a std::variant (that can contain values of different types), or a std::any (that can contain generic values), but a type that is determined independently from the received value.

Different if you pass the index of type/value you want (the first argument, in your case) as a template parameter.

I mean: different if you call find_item() as follows

find_item<1>(-1, "hello", 'Z', 10.03);
find_item<2>(-1, "hello", 'Z', 10.03);
find_item<3>(-1, "hello", 'Z', 10.03);

Now find_item<1>() can return a char const *, find_item<2>() can return a char and find_item<3>() can return a double.

This is because find_item<1>(), find_item<2>() and find_item<3>() are different functions, so can have different return types.

But... this way... we've almost obtained the std::get<>() that extract values from a std::tuple.

Unfortunately you can use this solution only when the index (the template parameter) is known compile time.

In other words, you can't make something as follows

for ( auto i = 1 ; i < 4 ; ++i )
{ // .............NO! ---V
auto value = find_item<i>(-1, "hello", 'Z', 10.03);
}



Related Topics



Leave a reply



Submit