强制使用特定类型的可变模板

Enforce variadic template of certain type

本文关键字:类型      更新时间:2023-10-16

我想强制可变模板的类型与先前设置的模板类型相同。在下面的例子中,我希望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");
}

我们可以使用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来自c++ 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>...>;

std::conjunction(逻辑与)在c++ 17中引入,因此不再需要手动实现all_same。然后构造函数变得简单:

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;
    // [...]   
}

c++的20个概念使它像

一样简单
    template<std::same_as<T>... U>
    Foo(T first, U... vals) {
        std::cout << "Called multiple argument ctor" << std::endl;
        // [...]   
    }
https://gcc.godbolt.org/z/neEsvo

如果要求所有参数为相同类型且参数个数可变,

可变模板对于这些要求来说太重了,只需使用 c++ 11 std::initializer_list

如果你能在调用时用{}替换(),它们就能完成任务。

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" };
}

在不实现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>