Reputation: 1326
I'm new to variadic templates and for the sake of learning consider the following function
template <typename T, typename... args>
T* make_arr(args... arg) {
// Code to check if passed args are of the same type
T* arr = new T[sizeof...(arg)]{ arg... };
return arr;
}
I have two questions:
args...
, I mean without using <typename T>
? ... I used decltype(arg) but it didnt work ...Note: please edit the title question if it is not appropriate ... thanks
Upvotes: 4
Views: 2319
Reputation: 27183
Start with a constexpr bool
function to check if all the booleans are true. This will be useful when checking that all the is_same
calls are true
.
constexpr bool all() {
return true;
}
template<typename ...B>
constexpr bool all(bool b, B... bs) {
return b && all(bs...);
}
Anyway, here is the make_arr
function:
template <typename... args
, class ...
, typename T = std::tuple_element_t<0, std::tuple<args...>>
, typename = std::enable_if_t< all(std::is_same<T, args>{}...) >
>
T* make_arr(args&&... arg) {
static_assert( all(std::is_same<T, args>{}...) ,"");
T* arr = new T[sizeof...(arg)]{ std::forward<args>(arg)... };
return arr;
}
A few comments:
&&
and std::forward
, to avoid potential copies if your type is large.std::tuple
type and using std::tuple_element<0, ..>
.static_assert
is used, where each type is compared to the first type (via is_same
).typename = std::enable_if_t< ..boolean-expression.. >
class ...
is essentially redundant. It's only purpose is to make it impossible for developers to cheat the checks by manually specifying the types (make_arr<int,char,size_t,bool>(..)
). However, maybe this is too conservative - the static_assert
will catch them anyway!Upvotes: 2
Reputation: 2982
First of all, you'll need these includes:
#include <type_traits>
#include <tuple>
then, let us declare variadic template to detect whether types are same or not:
template <typename ... args>
struct all_same : public std::false_type {};
template <typename T>
struct all_same<T> : public std::true_type {};
template <typename T, typename ... args>
struct all_same<T, T, args...> : public all_same<T, args ... > {};
now we can use static_assert
to detect if parameters types are same:
template <typename T, typename... args>
T* make_arr(args... arg) {
// Code to check if passed args are of the same type
static_assert(all_same<args ...>::value, "Params must have same types");
T* arr = new T[sizeof...(arg)]{ arg... };
return arr;
};
Finally, let us take return type of your function as first type of parameters - if all types are same we can take any of them. We use std::tuple
for this
template <typename... args>
typename std::tuple_element<0, std::tuple<args...> >::type * make_arr(args... arg) {
// Code to check if passed args are of the same type
static_assert(all_same<args ...>::value, "Params must have same types");
typedef typename std::tuple_element<0, std::tuple<args...> >::type T;
T* arr = new T[sizeof...(arg)]{ arg... };
return arr;
};
Upvotes: 3
Reputation: 49028
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
Upvotes: 4