是否可以将 'enable_if' 和 'is_same' 与可变函数模板一起使用?

Is it possible to use 'enable_if' and 'is_same' with variadic function templates?

编译这两个非可变函数模板:

template <typename T, typename U>
typename std::enable_if<std::is_same<U, int>::value, void>::
type testFunction(T a, U b) {
    std::cout << "b is integer\n";
}

template <typename T, typename U>
typename std::enable_if<std::is_same<U, float>::value, void>::
type testFunction(T a, U b) {
    std::cout << "b is float\n";
}

但是,类似的可变参数模板无法编译:

template <typename T, typename... U>
typename std::enable_if<std::is_same<U, int>::value, void>::
type testFunction(T a, U... bs) {
    std::cout << "bs are integers\n";
}

template <typename T, typename... U>
typename std::enable_if<std::is_same<U, float>::value, void>::
type testFunction(T a, U... bs) {
    std::cout << "bs are floats\n";
}

也许我正在尝试做一些无法完成的事情。我知道使用初始化列表可以实现类似的功能,但我想避免初始化列表参数所需的大括号。

是的。您可以在 C++17 中使用 fold expression

template <typename T, typename... U>
typename std::enable_if<(std::is_same<U, float>::value && ...), void>::
type testFunction(T a, U... bs) {
    std::cout << "bs are floats\n";
}

在 C++11 中,您可以重新实现 std::conjunction:

template<class...> struct conjunction : std::true_type { };
template<class B1> struct conjunction<B1> : B1 { };
template<class B1, class... Bn>
struct conjunction<B1, Bn...> 
    : std::conditional_t<bool(B1::value), conjunction<Bn...>, B1> {};

template <typename T, typename... U>
typename std::enable_if<
    std::conjunction_v<std::is_same<U, float>...>, 
    void
>::type testFunction(T a, U... bs) {
    std::cout << "bs are floats\n";
}

如果您绑定到 C++ 11 并希望保持代码的可读性,您可以实现 is_same 的简单等价物,它匹配多种类型:

template <typename Ref, typename T1, typename... TN>
struct all_match;

template <typename Ref, typename T>
struct all_match<Ref,T>
{
    static constexpr bool value = std::is_same<T,Ref>::value;
};

template <typename Ref, typename T1, typename... TN>
struct all_match
{
    static constexpr bool value = std::is_same<T1,Ref>::value && all_match<Ref, TN...>::value;
};

然后(注意,引用类型在前):

template <typename T, typename... U>
typename std::enable_if<all_match<int, U...>::value, void>::
type testFunction(T a, U... bs) {
    std::cout << "bs are integers\n";
}

现场演示:click.

C++ 17 引入了 fold expression,它允许您在二元运算符上折叠参数包 (...)。您可以使用它为参数包中的所有类型轻松应用 std::is_same<>,然后 and 值:

template <typename T, typename... U>
typename std::enable_if<(std::is_same<U, float>::value && ...), void>::
type testFunction(T a, U... bs) {
    std::cout << "bs are floats\n";
}

template <typename T, typename... U>
typename std::enable_if<(std::is_same<U, int>::value && ...), void>::
type testFunction(T a, U... bs) {
    std::cout << "bs are ints\n";
}

您可以查看此版本的演示 here

或者您可以简单地使用额外的可变参数 pack 来测试所有模板参数是否相同并等于给定类型 (C++11):

#include <type_traits>
#include <iostream>

template <class...>
struct pack { };

template <typename T, typename... U>
typename std::enable_if<std::is_same<pack<int, U...>, pack<U..., int>>::value, void>::
type testFunction(T a, U... bs) {
    std::cout << "bs are integers\n";
}

template <typename T, typename... U>
typename std::enable_if<std::is_same<pack<float, U...>, pack<U..., float>>::value, void>::
type testFunction(T a, U... bs) {
    std::cout << "bs are floats\n";
}

int main() {
    testFunction(1, 2, 3, 4, 5);
    testFunction(1, 2.0f, 3.5f, 4.4f, 5.3f);
}

[live demo]

输出:

bs are integers
bs are floats