Check Traits for All Variadic Template Arguments

Check traits for all variadic template arguments

Define all_true as

template <bool...> struct bool_pack;

template <bool... v>
using all_true = std::is_same<bool_pack<true, v...>, bool_pack<v..., true>>;

And rewrite your constructor to

// Check convertibility to B&; also, use the fact that getA() is non-const
template<typename... Args,
typename = std::enable_if_t<all_true<std::is_convertible<Args&, B&>{}...>>
C(Args&... args) :
member{args.getA()...}
{}

Alternatively, under C++17,

template<typename... Args,
typename = std::enable_if_t<(std::is_convertible_v<Args&, B&> && ...)>>
C(Args&... args) :
member{args.getA()...}
{}

I am afraid that if I use a predicate like std::is_base_of, I will get
a different instantiation of the constructor for each set of
parameters, which could increase compiled code size...

enable_if_t<…> will always yield the type void (with only one template argument given), so this cannot be is_base_ofs fault. However, when Args has different types, i.e. the types of the arguments are distinct, then subsequently different specializations will be instantiated. I would expect a compiler to optimize here though.


If you want the constructor to take precisely N arguments, you can use a somewhat easier method. Define

template <std::size_t, typename T>
using ignore_val = T;

And now partially specialize C as

// Unused primary template
template <size_t N, typename=std::make_index_sequence<N>> class C;
// Partial specialization
template <size_t N, std::size_t... indices>
class C<N, std::index_sequence<indices...>>
{ /* … */ };

The definition of the constructor inside the partial specialization now becomes trivial

C(ignore_val<indices, B&>... args) :
member{args.getA()...}
{}

Also, you do not have to worry about a ton of specializations anymore.

Using type traits with variadic template arguments

You don't need std::tuple to do that and you can restrict your function to copy const char* only using std::enable_if, as follows

#include <iostream>
#include <type_traits>

template<typename ... Args>
constexpr std::enable_if_t<std::is_same_v<std::common_type_t<Args...>,const char*>, void>
print(Args&&... args)
{
((std::cout << args << " "),...);
std::cout << '\n';
}
int main( )
{
print("this", "is", "a", "thing");
print("this", "is", "not", "a", "thing");
print(5, "is", "not", "a", "thing");//compile error
return 0;
}

C++11 Check two sets of variadic template arguments match

Here is a metaprogram I quickly came up with. It is a bit coarse, but can be implemented in a more better way. You should probably use the decayed type (std::decay) in the metaprogram to get correct result.

#include <iostream>
#include <type_traits>

template <typename... T> struct param_pack {};

template <typename, typename> struct is_all_same_impl;

template <>
struct is_all_same_impl<param_pack<>, param_pack<>>
{
static bool const value = true;
};

template <typename T, typename S, typename... Rest, typename... SRest>
struct is_all_same_impl<param_pack<T, Rest...>, param_pack<S, SRest...>>
{
static bool const value = false;
};

template <typename T, typename... Rest, typename... SRest>
struct is_all_same_impl<param_pack<T, Rest...>, param_pack<T, SRest...>>
{
static bool const value = is_all_same_impl<param_pack<Rest...>, param_pack<SRest...>>::value;
};

template <typename, typename>
struct is_all_same;

template <typename... FSet, typename... SSet>
struct is_all_same<param_pack<FSet...>, param_pack<SSet...>>: is_all_same_impl<param_pack<FSet...>, param_pack<SSet...>> {};

int main() {
std::cout << is_all_same<param_pack<int, char, float>, param_pack<int, char, int>>::value << std::endl;
return 0;
}

UPDATE :: More simpler version

template <typename... T> struct param_pack {};

int main() {
std::cout << std::is_same<param_pack<int, float, int>, param_pack<int,float,int>>::value << std::endl;
return 0;
}

So you can do something like:

static_assert( is_same<param_pack<Args...>, param_pack<std::decay_t<Dargs>...>>::value, "Parameters do not sufficiently match." );

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.

How to check that all types in variadic template are convertible to size_t?

With help from the really slick all_true trick from Columbo, it's a breeze :

template <bool...> struct bool_pack;
template <bool... v>
using all_true = std::is_same<bool_pack<true, v...>, bool_pack<v..., true>>;

template <class... Args>
std::enable_if_t<
all_true<std::is_convertible<Args, std::size_t>{}...>{}
> check(Args... args) {}

Live on Coliru

And in the specific case where Check is a constructor:

template<typename... Args, class = std::enable_if_t<all_true<std::is_convertible<Args, std::size_t>{}...>{}>>
explicit Check(Args... args) {}

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

Check a parameter pack for all of type T

Your syntax is just off a bit, you don't need two separate template declarations, that syntax is for defining member templates out-of-class:

template<typename Target, typename... Ts>
using areT = and_<std::is_same<Ts,Target>...>;

static_assert(areT<int,int,int,int>::value,"wat");
static_assert(!areT<int,float,int,int>::value,"wat");

Demo



Related Topics



Leave a reply



Submit