Принудительно использовать вариационный шаблон определенного типа

Я бы хотел, чтобы тип вариационного шаблона был идентичен типу более раннего заданного шаблона. В приведенном ниже примере я хотел бы, чтобы T и U были одного типа.

код на 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

Мы можем использовать SFINAE, чтобы все типы U были такими же, как T. Важно отметить, что U - это не только один тип, как вы подразумеваете, но и список возможных разнородных типов.

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 - из С++ 14. Если это не вариант для вас, просто используйте std::enable_if.

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

all_same может быть реализован несколькими путями. Здесь мне нравится использовать булевские пакеты:

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

Без реализации all_same вы также можете изменить код конструктора следующим образом:

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 - это функция в STL <type_traits>