How to set the same type for all arguments in this example?

孤者浪人 提交于 2019-12-08 03:30:49

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!