使用 enable_if 检查参数包的类型
Checking type of parameter pack using enable_if
由于 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
作为未命名参数的默认值,因为我的函数实际上是一个构造函数,不会有 return 值。这适用于单个参数,但由于它是一个可变参数模板 T
是一个参数包,所以上面的代码无效。那么,如何检查每个参数是否可以转换为双精度?
我认为更简单的方法是使用 std::initializer_list
:
foo(std::initializer_list<double> args)
{
// Your stuff.
}
而不是可变参数模板。
除了 ()
之外,可能还需要使用 {}
而不是/
再次使用 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 */}
这是一种通用方法——用于二进制折叠的 TMP,使用 C++14。首先,让我们定义基本的组合操作:
#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>);
这是另一个(c++11) version (heavily inspired by the 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 */}
您可以在 c++17 中使用 fold expression 来执行与此处发布的其他答案相同的操作,但无需创建模板的麻烦。
#include <type_traits>
template <typename... T, typename =
typename std::enable_if<
(true && ... && std::is_convertible_v<T, ___YOUR_TYPE___>),
void
>::type
>
constexpr auto foo(T...) noexcept {
// your code
}
由于 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
作为未命名参数的默认值,因为我的函数实际上是一个构造函数,不会有 return 值。这适用于单个参数,但由于它是一个可变参数模板 T
是一个参数包,所以上面的代码无效。那么,如何检查每个参数是否可以转换为双精度?
我认为更简单的方法是使用 std::initializer_list
:
foo(std::initializer_list<double> args)
{
// Your stuff.
}
而不是可变参数模板。
除了 ()
{}
而不是/
再次使用 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 */}
这是一种通用方法——用于二进制折叠的 TMP,使用 C++14。首先,让我们定义基本的组合操作:
#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>);
这是另一个(c++11) version (heavily inspired by the 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 */}
您可以在 c++17 中使用 fold expression 来执行与此处发布的其他答案相同的操作,但无需创建模板的麻烦。
#include <type_traits>
template <typename... T, typename =
typename std::enable_if<
(true && ... && std::is_convertible_v<T, ___YOUR_TYPE___>),
void
>::type
>
constexpr auto foo(T...) noexcept {
// your code
}