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

淺唱寂寞╮ 提交于 2019-11-29 07:13:34

Yes. You can use a fold expression in C++17:

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

In C++11, you can reimplement 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";
}

If you are bound to C++ 11 and want to keep your code readable, you can implement simple equivalent of is_same which matches multiple types:

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

And then (note, that the reference type goes first):

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

Live demo: click.

C++ 17 introduces fold expression which allows you to fold parameter pack (...) over a binary operator. You can use it to easy apply std::is_same<> for all types in a parameter pack and then and the values:

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

You can check demo for this version here.

Or you could simply make use of additional variadic pack to test if all template arguments are the same and equal to a given type (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]

Output:

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