找出C++对象是否可调用

Ant*_*ine 32 c++ metaprogramming traits type-traits

是否有可能写出一个类型特征,比如说is_callable<T>一个对象是否已operator()定义?如果调用运算符的参数事先已知,则很容易,但在一般情况下则不行.当且仅当至少有一个重载调用运算符被定义时,我希望特征返回true.

这个问题是相关的,并且有一个很好的答案,但它不适用于所有类型(仅限于 - 可int转换类型).此外,std::is_function工作,但只适用于正确的C++函数,而不是函子.我正在寻找更通用的解决方案.

jro*_*rok 35

我认为这个特性可以满足您的需求.它可以检测到operator()任何类型的签名,即使它已经过载也是如果它被模板化了:

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

    struct Fallback { void operator()(); };
    struct Derived : T, Fallback { };

    template<typename U, U> struct Check;

    template<typename>
    static yes test(...);

    template<typename C>
    static no test(Check<void (Fallback::*)(), &C::operator()>*);

public:
    static const bool value = sizeof(test<Derived>(0)) == sizeof(yes);
};
Run Code Online (Sandbox Code Playgroud)

该原则基于Member Detector习语.实际上,如果你传递一个非类型的类型,它将无法编译,但这不应该很难解决,我只是为了简洁而将它留下.您还可以将其扩展为报告函数的true.

当然它没有给你任何关于签名的任何信息operator(),但我相信这不是你要求的,对吧?

编辑Klaim:

它非常简单,可以false使用非类类型工作(返回).如果将上述类重命名为is_callable_impl,则可以编写此类,例如:

template<typename T>
struct is_callable
    : std::conditional<
        std::is_class<T>::value,
        is_callable_impl<T>,
        std::false_type
    >::type
{ };
Run Code Online (Sandbox Code Playgroud)

  • @Klaim看到编辑,希望有所帮助. (2认同)

Cha*_*acy 10

这里的答案很有帮助,但我来到这里想要的东西也可以发现某些东西是否可以调用,无论它是否恰好是一个对象或一个经典函数. jrok对问题的这个方面的答案,唉,没有用,因为std::conditional实际上评估了双臂的类型!

所以,这是一个解决方案:

// Note that std::is_function says that pointers to functions
// and references to functions aren't functions, so we'll make our 
// own is_function_t that pulls off any pointer/reference first.

template<typename T>
using remove_ref_t = typename std::remove_reference<T>::type;

template<typename T>
using remove_refptr_t = typename std::remove_pointer<remove_ref_t<T>>::type;

template<typename T>
using is_function_t = typename std::is_function<remove_refptr_t<T>>::type;

// We can't use std::conditional because it (apparently) must determine
// the types of both arms of the condition, so we do it directly.

// Non-objects are callable only if they are functions.

template<bool isObject, typename T>
struct is_callable_impl : public is_function_t<T> {};

// Objects are callable if they have an operator().  We use a method check
// to find out.

template<typename T>
struct is_callable_impl<true, T> {
private:
    struct Fallback { void operator()(); };
    struct Derived : T, Fallback { };

    template<typename U, U> struct Check;

    template<typename>
    static std::true_type test(...);

    template<typename C>
    static std::false_type test(Check<void (Fallback::*)(), &C::operator()>*);

public:
    typedef decltype(test<Derived>(nullptr)) type;
};


// Now we have our final version of is_callable_t.  Again, we have to take
// care with references because std::is_class says "No" if we give it a
// reference to a class.

template<typename T>
using is_callable_t = 
    typename is_callable_impl<std::is_class<remove_ref_t<T>>::value,
                              remove_ref_t<T> >::type;
Run Code Online (Sandbox Code Playgroud)

但最后,对于我的应用程序,我真的只想知道你是否可以说f()(即,没有参数调用它),所以我改为使用更简单的东西.

template <typename T>
constexpr bool noarg_callable_impl(
    typename std::enable_if<bool(sizeof((std::declval<T>()(),0)))>::type*)
{
    return true;
}

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

template<typename T>
constexpr bool is_noarg_callable()
{
    return noarg_callable_impl<T>(nullptr);
}
Run Code Online (Sandbox Code Playgroud)

事实上,我走得更远了.我知道这个函数应该返回一个int,所以不要只是检查我是否可以调用它,我也检查了返回类型,通过更改enable_if为:

    typename std::enable_if<std::is_convertible<decltype(std::declval<T>()()),
                                                int>::value>::type*)
Run Code Online (Sandbox Code Playgroud)

希望这有助于某人!


And*_*owl 9

这是一个使用C++ 11的可能解决方案,无需知道仿函数的调用运算符的签名即可工作,但只有仿函数没有多个重载operator ():

#include <type_traits>

template<typename T, typename = void>
struct is_callable : std::is_function<T> { };

template<typename T>
struct is_callable<T, typename std::enable_if<
    std::is_same<decltype(void(&T::operator())), void>::value
    >::type> : std::true_type { };
Run Code Online (Sandbox Code Playgroud)

这是你如何使用它:

struct C
{
    void operator () () { }
};

struct NC { };

struct D
{
    void operator () () { }
    void operator () (int) { }
};

int main()
{
    static_assert(is_callable<C>::value, "Error");
    static_assert(is_callable<void()>::value, "Error");

    auto l = [] () { };
    static_assert(is_callable<decltype(l)>::value, "Error");

    // Fires! (no operator())
    static_assert(is_callable<NC>::value, "Error");

    // Fires! (several overloads of operator ())
    static_assert(is_callable<D>::value, "Error");
}
Run Code Online (Sandbox Code Playgroud)

这是一个实例.


log*_*cor 5

C++17 带来了std::is_invocable和朋友。

这个答案还给出了如何用 C++14 模拟它的解决方案。