问题
Just for practice I'm trying to write a variadic template that inputs some values into vector. I wrote the following:
template <class T>
void add(vector<T> *v, T n){
v->push_back(n);
}
template <class T, class... T2>
void add(vector<T> *v, T n, T2... rest){
v->push_back(n);
add(v, rest...);
}
To test these I use the following:
vector<int> vI;
add(&vI, 10, 30, 25);
for (int i = 0; i < vI.size(); i++)
cout << vI[i] << endl;
Everything works as expected, but I'm wondering if the second template could be written in a way that it uses only one type (T or T2) since vector (push_back) expects the same type for all arguments? In fact, I would like to ensure T = T2 all the way.
回答1:
The funny thing is that your current code already ensures that calls like
add(&vI, 10, 10.f, 20);
doesn't compile. If an argument's type is different from the vector's value type, then eventually it will become the second argument in a call like
add(&vI, 10.f, 20);
and then template argument deduction will fail because it deduces conflicting types for T
.
If you want to reduce the horrendous amount of error messages when the deduction failure happens deep in the recursion, SFINAE or static_assert
can help. I personally prefer Columbo's trick:
template<bool...> class bool_pack;
template<bool...b>
using all_true = std::is_same<bool_pack<true, b...>, bool_pack<b..., true>>;
template <class T, class... T2>
void add(vector<T> *v, T n, T2... rest)
{
static_assert(all_true<std::is_same<T, T2>{}...>{}, "T2 must be all Ts");
v->push_back(n);
add(v, rest...);
}
There is currently no way to avoid using both T and T2 (without modifying the call). The committee is considering potential improvements in this area, however, so something might make its way into the next standard.
回答2:
You can create a metafunction which ensures that all the types in the parameter pack T2
are equal to T
.
template <typename T, typename ...Ts>
struct AllSame : public std::false_type{};
template <typename T>
struct AllSame<T> : public std::true_type{};
template <typename T, typename U, typename ...Ts>
struct AllSame<T, U, Ts...> : public std::conditional_t<std::is_same<T,U>::value, AllSame<T, Ts...>, std::false_type>{};
Now use SFINAE on your return type:
template <class T, class... T2>
auto add(vector<T> *v, T n, T2... rest)
-> std::enable_if_t<AllSame<T, T2...>::value>
{
v->push_back(n);
add(v, rest...);
}
来源:https://stackoverflow.com/questions/28864200/how-to-set-the-same-type-for-all-arguments-in-this-example