2015-10-13 14 views
1

我使用下面的方式,在计算器回答呈现以确定类型是否是可调用或不:任何重载的运算符()的静态检测

template <typename T> 
struct is_callable 
{ 
    typedef char (& yes)[1]; 
    typedef char (& no)[2]; 

    // we need a template here to enable SFINAE 
    template <typename U> 
    static yes deduce(char (*)[sizeof(&U::operator())]); 
    // fallback 
    template <typename> static no deduce(...); 

    static bool constexpr value = std::is_function<T>::value || (sizeof(deduce<T>(0)) == sizeof(yes)); 
}; 

当一个类具有一个以上的过载然而它失败运算符()的:

#include <iostream> 

template <typename T> 
class A 
{ 
    T operator()(); 
    T operator()(T a); 
    T operator()(T a, T b); 
}; 

std::cout << is_callable<A<int>>::value; // false 

是否有可能确定一个类型是否具有任何变体(模板或没有,一个或多个)重载操作的()?

+0

你打算如何使用这些信息?在大多数情况下,您实际上并不关心它是否可以调用,您只关心它是否可以用想要传递给它的特定参数进行调用,并且这已经很容易检测到。 – hvd

+1

我问的原因是因为这是不可能的,但取决于你具体做了什么,以及你可以对将要测试的类型做出什么假设,还有其他可能。如果你会检查我的答案历史记录,你会看到我回答C++问题,包括有关使用模板来检测类型特征的问题。但如果你更高兴地认为我会破坏你的经验,那就去吧,我不希望被指责试图控制你的想法。 – hvd

+0

我也很好奇所需的用例。充其量,我可以写一些东西来检查一个类型是否可以用最多N个参数进行调用,并且对这些参数最终会产生什么限制。 – Barry

回答

0

首先,您需要声明A为完整类型(例如,A<int>)。

这不是relyable检查对象是否invokeable或不只是通过展开,并比较它的operator(),因为它会在模板或重载函数子失败。

using the following piece of code 以检测对象是否是可调用与给定的函数签名(ReturnType(Arguments...)

这工作也可用于过载或模板operator()的,因为代码试图具有给定参数 代替调用该对象比较其解开的签名operator()

作为小的添加,如果对象可以从const,volatile或rvalue上下文中调用,也可以检查该对象。

#include <type_traits> 

template<typename Fn> 
struct impl_is_callable_with_qualifiers; 

template<typename ReturnType, typename... Args> 
struct impl_is_callable_with_qualifiers<ReturnType(Args...)> 
{ 
    template<typename T> 
    static auto test(int) 
     -> typename std::is_convertible< 
      decltype(std::declval<T>()(std::declval<Args>()...)), 
      ReturnType 
      >; 

    template<typename T> 
    static auto test(...) 
     -> std::false_type; 
}; 

template<bool Condition, typename T> 
using add_const_if_t = typename std::conditional< 
    Condition, 
    typename std::add_const<T>::type, 
    T 
>::type; 

template<bool Condition, typename T> 
using add_volatile_if_t = typename std::conditional< 
    Condition, 
    typename std::add_volatile<T>::type, 
    T 
>::type; 

template<bool Condition, typename T> 
using add_lvalue_if_t = typename std::conditional< 
    Condition, 
    typename std::add_lvalue_reference<T>::type, 
    T 
>::type; 

template<typename T, typename Fn, bool Const, bool Volatile, bool RValue> 
using is_callable_with_qualifiers = decltype(impl_is_callable_with_qualifiers<Fn>::template test< 
    add_lvalue_if_t<!RValue, 
     add_volatile_if_t<Volatile, 
      add_const_if_t<Const, 
       typename std::decay<T>::type>>> 
>(0)); 

这是不可能确定一个类型是否有超载operator(), 的任何变体,但是你可以测试特定类型或执行多种类型的测试,以检测“可能”的模板。

覆盖从你的问题的代码将是一个例子:

#include <iostream> 

template<typename T> 
class A 
{ 
public: 
    A() = default; 

    T operator()() { return T(); } 
    T operator()(T a) { return T(); } 
    T operator()(T a, T b) { return T(); } 
}; 

template <typename TheClass, typename T> 
using is_callable = is_callable_with_qualifiers<TheClass, T(T), false, false, false>; 

int main() 
{ 
    std::cout << is_callable<A<int>, int>::value; // true 
} 

Demo

+0

我认为这个问题可能已经足够好了。 OP对我的评论不满意。 – hvd

0

如果你只需要检查,如果不服可调用 - 尝试调用它,看看会发生什么:

template <typename T, typename RetType = decltype(std::declval<T>()())> 
constexpr bool check_is_callable(int) 
{ 
    return true; 
} 

template <typename> 
constexpr bool check_is_callable(...) 
{ 
    return false; 
} 

template <typename T> 
constexpr bool is_callable() 
{ 
    return check_is_callable<T>(0); 
} 

这里std::declval<T>()越来越T形的对象,std::declval<T>()()试图调用T类型的对象,如果成功,RetType = decltype(std::declval<T>()())获得调用的返回类型。如果没有,SFIANE会让其他功能超载。