使用enable_if检查参数包的类型

问题描述 投票:12回答:4

由于对allowed non-type variadic templates有限制,我正在尝试使用enable_if编写一个使用任意数量的双精度函数。从本质上讲,我想做的事情如下:

    template<typename... T,
    typename = typename std::enable_if<std::is_convertible<T, double>::value, T>::type>
    foo(T... t){ /* code here */ }

我选择将enable_if作为未命名参数的默认值,因为我的函数实际上是一个构造函数,并且没有返回值。这适用于单个参数,但由于它是可变参数模板,因此T是参数包,并且上述代码无效。那么,我如何检查每个参数是否可以转换为double?

c++ templates variadic-templates
4个回答
18
投票

bool_pack再次欺骗。

template<bool...> struct bool_pack;
template<bool... bs> 
using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>;

然后

template<class R, class... Ts>
using are_all_convertible = all_true<std::is_convertible<Ts, R>::value...>;

最后

template<typename... T,
typename = typename enable_if<are_all_convertible<double, T...>::value>::type>
foo(T... t){ /* code here */}

2
投票

我认为更简单的是使用std::initializer_list

foo(std::initializer_list<double> args)
{
    // Your stuff.
}

而不是可变参数模板。除了{}之外,它可能需要使用()而不是/


1
投票

这是另一个()版本(很大程度上受到T.C.上面一个版本的启发):

#include <type_traits>

template <typename To, typename From, typename... R>
struct are_all_convertible {
    constexpr static bool value = std::is_convertible<From,To>::value &&
                                  are_all_convertible<To,R...>::value;
};

template <typename To, typename From>
struct are_all_convertible<To,From> {
    constexpr static bool value = std::is_convertible<From,To>::value;
};

template<typename... T,
typename = typename std::enable_if<are_all_convertible<double, T...>::value>::type>
foo(T... t){ /* code here */}

0
投票

这是一种通用方法 - 使用C ++ 14进行二进制折叠的TMP。首先,让我们定义基本的组合操作:

#include <type_traits>

struct and_op
{
    using type = bool;
    using identity = std::true_type;
    template <bool A, bool B> static constexpr bool value = A && B;
};

struct or_op
{
    using type = bool;
    using identity = std::false_type;
    template <bool A, bool B> static constexpr bool value = A || B;
};

现在真正的fold机制:

template <typename Op, typename Op::type...>
struct fold;

template <typename Op>
struct fold<Op> : Op::identity {};

template <typename Op, typename Op::type Val>
struct fold<Op, Val>
    : std::integral_constant<typename Op::type
    , Val> {};

template <typename Op, typename Op::type Val, typename Op::type... Tail>
struct fold<Op, Val, Tail...>
    : std::integral_constant<typename Op::type
    , Op::template value<Val, fold<Op, Tail...>::value>> {};

接下来,我们需要一种通过绑定从二进制特征创建一元特征的方法:

template <template <typename, typename> class BPred, typename T>
struct bind_pred
{
    template <typename U>
    struct pred_1st : std::integral_constant<bool, BPred<T, U>::value> {};
    template <typename U>
    struct pred_2nd : std::integral_constant<bool, BPred<U, T>::value> {};
};

最后,帮助包装器结合应用一元谓词的结果:

template <typename Op, template <typename> class UPred, typename ...Args>
struct fold_pred : fold<Op, UPred<Args>::value...> {};

而已。现在让我们开始工作:

template <typename T>
using maybe_double = bind_pred<std::is_convertible, double>::pred_2nd<T>;

#include <iomanip>
#include <iostream>

int main()
{
    std::cout
        << std::boolalpha
        << fold_pred<and_op, maybe_double, int, float>::value << '\n'
        << fold_pred<and_op, maybe_double, int, float, void>::value << '\n';
}

在C ++ 17(或者C ++ 1z)中,由于新的折叠表达式,您可以使用更少的代码编写直接解决方案。例如:

template <template <typename> class UPred, typename ...Args>
static constexpr bool pred_all = (UPred<Args>::value && ...);
//                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^ unary fold

用法:

static_assert(pred_all<maybe_double, int, float>);
© www.soinside.com 2019 - 2024. All rights reserved.