Enforce variadic template of certain type

烈酒焚心 提交于 2019-12-04 10:05:35

问题


I would like to enforce the type of variadic template to be identical to an earlier set template type. In the below example, I'd like T and U to be the same type.

code on ideone.com

#include <iostream>
#include <string>

template<class T>
struct Foo {

    Foo(T val) {
        std::cout << "Called single argument ctor" << std::endl;
        // [...]    
    }    

    // How to enforce U to be the same type as T?
    template<class... U>
    Foo(T first, U... vals) {
        std::cout << "Called multiple argument ctor" << std::endl;
        // [...]   
    }

};

int main() {

    // Should work as expected.
    Foo<int> single(1);

    // Should work as expected.
    Foo<int> multiple(1, 2, 3, 4, 5);

    // Should't work (but works right now). The strings are not integers.
    Foo<int> mixedtype(1, "a", "b", "c");

    // Also shouldn't work. (doesn't work right now, so that is good)
    Foo<int> alsomixedtype(1, 1, "b", "c");
}

回答1:


We can use SFINAE to ensure that all U types are the same as T. An important thing to note is that U is not just one type as you imply, but a list of possibly disparate types.

template<class... U, std::enable_if_t<all_same<T, U...>::value>* = nullptr>
Foo(T first, U... vals) {
    std::cout << "Called multiple argument ctor" << std::endl;
    // [...]   
}

std::enable_if_t is from C++14. If that's not an option for you, just use std::enable_if.

typename std::enable_if<all_same<T, U...>::value>::type* = nullptr>

all_same can be implemented in a bunch of different ways. Here's a method I like using boolean packs:

namespace detail
{
    template<bool...> struct bool_pack;
    template<bool... bs>
    //if any are false, they'll be shifted in the second version, so types won't match
    using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>;
}
template <typename... Ts>
using all_true = detail::all_true<Ts::value...>;

template <typename T, typename... Ts>
using all_same = all_true<std::is_same<T,Ts>...>;



回答2:


std::conjunction (logical AND) was introduced in C++17 so one doesn't have to implement all_same manually anymore. Then the constructor becomes simply:

template<typename... U,
    typename = std::enable_if_t<
        std::conjunction_v<
            std::is_same<T, U>...
        >
    >
>
Foo(T first, U... vals)
{
    std::cout << "Called multiple argument ctor" << std::endl;
    // [...]   
}

See live example.




回答3:


If all parameters are required to be the same type AND the number of parameters is variable,

Variadic templates are too heavy for those requirements, just use C++11 std::initializer_list.

They just do the job if you can replace () with {} on the call.

template<class T> struct Foo {
    Foo(T val) {
        std::cout << "Called single argument ctor" << std::endl;
    }
    // Enforce all parameters to be the same type :
    Foo( std::initializer_list<T> values ) {
        std::cout << "Called multiple argument ctor" << std::endl;
        for (T value : values)
            cout << value << endl;
    }
};

int main() {
    // Work as expected.
    Foo<int> single(1);
    // Work as expected.
    Foo<int> multiple{ 1, 2, 3, 4, 5 };
    // Doesn't work - as required :
    //Foo<int> mixedtype{ 1, "a", "b", "c" };

}



回答4:


Without implementing of all_same you could also change the constructor code as follows:

template<class F, typename = typename enable_if<is_same<F, T>::value>::type, class... U>
    Foo(F first, U... vals): Foo(vals...) {
    std::cout << "Called multiple argument ctor" << std::endl;
    // [...]   
}

is_same is the function in an STL <type_traits>



来源:https://stackoverflow.com/questions/30346652/enforce-variadic-template-of-certain-type

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