[英]C++ metafunction to determine whether a type is callable
是否可以編寫一個C ++(0x)元函數來確定一個類型是否可調用?
通過可調用類型我的意思是函數類型,函數指針類型,函數引用類型(這些由boost::function_types::is_callable_builtin
檢測),lambda類型,以及任何帶有重載operator()
類(也許任何具有隱式的類)轉換運算符到其中一個,但這不是絕對必要的)。
編輯 :元函數應檢測帶有任何簽名的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())將產生錯誤。 解決方案是首先對模板化運算符運行檢查序列,並且當且僅當找不到模板化運算符時才檢查常規運算符()。 如果找到模板化的檢查,則通過將非模板化檢查專門化為無操作來完成。
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總是一個,如上所述,那么檢查應該更簡單。 我認為沒有必要使用任何其他類型的特征或圖書館設施。
隨着我們在c ++ 11(以及更高版本)中的集體經驗的出現,現在可能是重新審視這個問題的時候了。
這種小型特質似乎對我有用:
#include <iostream>
#include <utility>
template<class F, class...Args>
struct is_callable
{
template<class U> static auto test(U* p) -> decltype((*p)(std::declval<Args>()...), void(), std::true_type());
template<class U> static auto test(...) -> decltype(std::false_type());
static constexpr bool value = decltype(test<F>(0))::value;
};
我們可以這樣測試:
template<class F, class...Args, typename std::enable_if<is_callable<F, Args&&...>::value>::type* = nullptr>
void test_call(F, Args&&...args)
{
std::cout << "callable" << std::endl;
}
template<class F, class...Args, typename std::enable_if<not is_callable<F, Args&&...>::value>::type* = nullptr>
void test_call(F, Args&&...args)
{
std::cout << "not callable" << std::endl;
}
extern void f3(int, const std::string&)
{
}
int main()
{
auto f1 = [](int, std::string) {};
test_call(f1, 0, "hello");
test_call(f1, "bad", "hello");
std::function<void(int, const std::string&)> f2;
test_call(f2, 0, "hello");
test_call(f2, "bad", "hello");
test_call(f3, 0, "hello");
test_call(f3, "bad", "hello");
}
預期產量:
callable
not callable
callable
not callable
callable
not callable
這是一個非常有趣的問題。 我一直很困惑。
我想我設法對Crazy Eddie的代碼進行了修改,允許任意數量的參數,但是,它確實使用了可變參數模板,它確實需要指定您希望調用“可調用”對象的參數。 長話短說,我得到了這個運行並按預期在gcc 4.6.0上工作:
編輯:也可以使用std :: result_of實用程序,但它不起作用,因為它需要一個typename
來消除std::result_of<..>::type
歧義,從而打破了Sfinae。
#include <iostream>
#include <type_traits>
template < typename PotentiallyCallable, typename... Args>
struct is_callable
{
typedef char (&no) [1];
typedef char (&yes) [2];
template < typename T > struct dummy;
template < typename CheckType>
static yes check(dummy<decltype(std::declval<CheckType>()(std::declval<Args>()...))> *);
template < typename CheckType>
static no check(...);
enum { value = sizeof(check<PotentiallyCallable>(0)) == sizeof(yes) };
};
int f1(int,double) { return 0; };
typedef int(*f1_type)(int,double) ; //this is just to have a type to feed the template.
struct Foo { };
struct Bar {
template <typename T>
void operator()(T) { };
};
int main() {
if( is_callable<f1_type,int,double>::value )
std::cout << "f1 is callable!" << std::endl;
if( is_callable<Foo>::value )
std::cout << "Foo is callable!" << std::endl;
if( is_callable<Bar,int>::value )
std::cout << "Bar is callable with int!" << std::endl;
if( is_callable<Bar,double>::value )
std::cout << "Bar is callable with double!" << std::endl;
};
我希望這是你正在尋找的東西,因為我認為不可能做得更多。
編輯:對於您的用例,它是一個部分解決方案,但它可能會有所幫助:
template <typename Predicate1, typename Predicate2>
struct and_predicate
{
template <typename ArgT>
enable_if<ice_and<is_callable<Predicate1,ArgT>::value,
is_callable<Predicate2,ArgT>::value>::value,
bool>::type 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, boost::any >::value,
is_callable< Predicate2, boost::any >::value>::value,
and_predicate<Predicate1, Predicate2>>::type
operator&&(Predicate1 predicate1, Predicate2 predicate2)
{
return and_predicate<Predicate1, Predicate2>{predicate1, predicate2};
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.