是否可以编写一个 C++(0x) 元函数来确定一个类型是否可调用?
可调用类型是指函数类型、函数指针类型、函数引用类型(这些由 boost::function_types::is_callable_builtin
检测)、lambda 类型以及任何具有重载 operator()
(也可能是任何具有隐式转换运算符到其中之一的类,但这不是绝对必要的)。
EDIT:元函数应检测是否存在带有任何签名的 operator()
,包括模板化的 operator()
。我相信这是困难的部分。
编辑:这是一个用例:
template <typename Predicate1, typename Predicate2>
struct and_predicate
{
template <typename ArgT>
bool operator()(const ArgT& arg)
{
return predicate1(arg) && predicate2(arg);
}
Predicate1 predicate1;
Predicate2 predicate2;
};
template <typename Predicate1, typename Predicate2>
enable_if<ice_and<is_callable<Predicate1>::value,
is_callable<Predicate2>::value>::value,
and_predicate<Predicate1, Predicate2>>::type
operator&&(Predicate1 predicate1, Predicate2 predicate2)
{
return and_predicate<Predicate1, Predicate2>{predicate1, predicate2};
}
is_callable
是我想要实现的。
最佳答案
可以通过以下方式检测给定类型 T 的非模板化 T::operator() 的存在:
template<typename C> // detect regular operator()
static char test(decltype(&C::operator()));
template<typename C> // worst match
static char (&test(...))[2];
static const bool value = (sizeof( test<T>(0) )
可以通过以下方式检测模板化运算符的存在:
template<typename F, typename A> // detect 1-arg operator()
static char test(int, decltype( (*(F*)0)( (*(A*)0) ) ) = 0);
template<typename F, typename A, typename B> // detect 2-arg operator()
static char test(int, decltype( (*(F*)0)( (*(A*)0), (*(B*)0) ) ) = 0);
// ... detect N-arg operator()
template<typename F, typename ...Args> // worst match
static char (&test(...))[2];
static const bool value = (sizeof( test<T, int>(0) ) == 1) ||
(sizeof( test<T, int, int>(0) ) == 1); // etc...
然而,这两个不能很好地结合在一起,因为如果 C 有一个模板化的函数调用运算符,decltype(&C::operator()) 会产生错误。解决方案是首先对模板化运算符运行一系列检查,然后检查常规 operator() 当且仅当 找不到模板化运算符。如果找到模板化检查,则通过将非模板化检查专门用于无操作来完成此操作。
template<bool, typename T>
struct has_regular_call_operator
{
template<typename C> // detect regular operator()
static char test(decltype(&C::operator()));
template<typename C> // worst match
static char (&test(...))[2];
static const bool value = (sizeof( test<T>(0) ) == 1);
};
template<typename T>
struct has_regular_call_operator<true,T>
{
static const bool value = true;
};
template<typename T>
struct has_call_operator
{
template<typename F, typename A> // detect 1-arg operator()
static char test(int, decltype( (*(F*)0)( (*(A*)0) ) ) = 0);
template<typename F, typename A, typename B> // detect 2-arg operator()
static char test(int, decltype( (*(F*)0)( (*(A*)0), (*(B*)0) ) ) = 0);
template<typename F, typename A, typename B, typename C> // detect 3-arg operator()
static char test(int, decltype( (*(F*)0)( (*(A*)0), (*(B*)0), (*(C*)0) ) ) = 0);
template<typename F, typename ...Args> // worst match
static char (&test(...))[2];
static const bool OneArg = (sizeof( test<T, int>(0) ) == 1);
static const bool TwoArg = (sizeof( test<T, int, int>(0) ) == 1);
static const bool ThreeArg = (sizeof( test<T, int, int, int>(0) ) == 1);
static const bool HasTemplatedOperator = OneArg || TwoArg || ThreeArg;
static const bool value = has_regular_call_operator<HasTemplatedOperator, T>::value;
};
如果arity 总是1,如上所述,那么检查应该更简单。我认为不需要任何额外的类型特征或库设施来使其工作。
关于确定类型是否可调用的 C++ 元函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5100015/