如何检查类型是否存在无参数运算符()

时间:2011-10-26 16:39:48

标签: c++ templates metaprogramming c++11

我正在尝试检查仿函数是否与给定的一组参数类型和给定的返回类型兼容(也就是说,给定的参数类型可以被隐含地转换为实际的参数类型,反之亦然的返回类型) 。目前我使用以下代码:

    template<typename T, typename R, template<typename U, typename V> class Comparer>
    struct check_type
    { enum {value = Comparer<T, R>::value}; };

    template<typename T, typename Return, typename... Args>
    struct is_functor_compatible
    {
        struct base: public T
        {
            using T::operator();
            std::false_type operator()(...)const;
        };
        enum {value = check_type<decltype(std::declval<base>()(std::declval<Args>()...)), Return, std::is_convertible>::value};
    };

check_type<T, V, Comparer> 这在大多数情况下非常好用,但是当我测试像struct foo{ int operator()() const;};之类的无参数函子时,它无法编译,因为在这种情况下,两个operator()的基数显然是不明显的,导致像这样:

error: call of '(is_functor_compatible<foo, void>::base) ()' is ambiguous
note: candidates are:
note: std::false_type is_functor_compatible<T, Return, Args>::base::operator()(...) const [with T = foo, Return = void, Args = {}, std::false_type = std::integral_constant<bool, false>]
note: int foo::operator()() const

所以很明显我需要一种不同的方法来检查无参数仿函数。我试图为is_functor_compatible部分专门化一个空参数包,在那里我检查&T::operator()的类型是否是无参数的成员函数,它或多或少有效。但是,当测试的仿函数有多个operator()时,这种方法显然会失败。

因此我的问题是,是否有更好的方法来测试无参数operator()的存在以及如何做到这一点。

2 个答案:

答案 0 :(得分:8)

当我想测试给定表达式是否对某个类型有效时,我使用类似于这个的结构:

template <typename T>
struct is_callable_without_parameters {
private:
    template <typename T1>
    static decltype(std::declval<T1>()(), void(), 0) test(int);
    template <typename>
    static void test(...);
public:
    enum { value = !std::is_void<decltype(test<T>(0))>::value };
};

答案 1 :(得分:3)

你有没有试过像:

template<size_t>
class Discrim
{
};

template<typename T>
std::true_type hasFunctionCallOper( T*, Discrim<sizeof(T()())>* );

template<typename T>
std::false_type hasFunctionCallOper( T*, ... );

之后,你区分了返回类型 hasFunctionCallOper((T*)0, 0)

已编辑(感谢R. Martinho Fernandes的建议):

以下是有效的代码:

template<size_t n>
class CallOpDiscrim {};

template<typename T>
TrueType hasCallOp( T*, CallOpDiscrim< sizeof( (*((T const*)0))(), 1 ) > const* );
template<typename T>
FalseType hasCallOp( T* ... );

template<typename T, bool hasCallOp>
class TestImpl;

template<typename T>
class TestImpl<T, false>
{
public:
    void doTellIt() { std::cout << typeid(T).name() << " does not have operator()" << std::endl; }
};

template<typename T>
class TestImpl<T, true>
{
public:
    void doTellIt() { std::cout << typeid(T).name() << " has operator()" << std::endl; }
};

template<typename T>
class Test : private TestImpl<T, sizeof(hasCallOp<T>(0, 0)) == sizeof(TrueType)>
{
public:
    void tellIt() { this->doTellIt(); }
};