【问题标题】:Howto check a type for the existence of parameterless operator()如何检查类型是否存在无参数运算符()
【发布时间】:2011-10-26 16:39:48
【问题描述】:

我正在尝试检查仿函数是否与给定的一组参数类型和给定的返回类型兼容(即,给定的参数类型可以隐式转换为实际的参数类型,而返回类型则相反) .目前我为此使用以下代码:

    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&lt;T, V, Comparer&gt; 这在大多数情况下工作得很好,但是当我测试像struct foo{ int operator()() const;}; 这样的无参数仿函数时它无法编译,因为在这种情况下,base 的两个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 的部分特化,在那里我检查&amp;T::operator() 的类型是否是无参数成员函数,它或多或少地起作用。然而,当被测函子有多个 operator() 时,这种方法显然会失败。

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

【问题讨论】:

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


    【解决方案1】:

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

    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 };
    };
    

    【讨论】:

    • 到目前为止似乎工作,但由于我想了解代码,你能解释一下这个 decltype(std::declval&lt;T1&gt;()(), void(), 0) 到底在做什么吗?
    • @Grizzly 第一部分是关于我们正在寻找的表达式的 SFINAE。最后的0 是有一个int 返回类型,因为如果std::declval&lt;T1&gt;()() 部分有void 作为返回类型,您将无法区分重载。而中间的void()是为了防止因为重载的逗号操作符而引起的讨厌的事情。
    • 也适用于非无参数情况(使用std::declval&lt;T1&gt;()(std::declval&lt;Args&gt;()...)),所以我什至不必专门研究无参数构造函数,非常感谢
    • @Grizzly 是的,这很容易适应你想要的任何表达方式:)
    【解决方案2】:

    你有没有尝试过:

    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(); }
    };
    

    【讨论】:

    • 这似乎不起作用,编译器不喜欢T()()(并不奇怪,所以我尝试使用std::declval&lt;T&gt;()(),但hasFunctionCallOper((T*)0, 0)似乎永远不会匹配第一个函数,甚至如果 operator() 存在
    • @Grizzly 我承认我没有测试它;我已经对命名函数使用了类似的东西。 (但也许sizeof 中的表达式更接近(*((T*)0))() 否则,如果也没有默认构造函数,它将失败。)例外的是,我在这里的回复只是对您可能会尝试的事情的建议;为了给出明确的答案,我没有时间亲自试验。
    • @Grizzly 好的。我已经测试过了。它适用于除 void 之外的所有返回值; sizeofvoid 不起作用。所以我会听从 R. Martinho Fernandes:使用 sizeof( (*((T const*)0))(), 1 ) 有效(或者如果你只想要非常量 operator(),则放弃 const)。我将编辑我的帖子以添加有效的代码(但真正归功于 Martinho,因为它使用逗号运算符后跟一个整数的想法)。
    猜你喜欢
    • 1970-01-01
    • 2020-01-26
    • 2014-06-17
    • 1970-01-01
    • 2020-04-25
    • 2015-09-03
    • 1970-01-01
    • 2011-06-08
    • 1970-01-01
    相关资源
    最近更新 更多