代码之家  ›  专栏  ›  技术社区  ›  James McNellis

为什么std::函数不相等?

  •  59
  • James McNellis  · 技术社区  · 14 年前

    这个问题也适用于 boost::function std::tr1::function .

    std::function 不具有可比性:

    #include <functional>
    void foo() { }
    
    int main() {
        std::function<void()> f(foo), g(foo);
        bool are_equal(f == g); // Error:  f and g are not equality comparable
    }
    

    operator== operator!= 重载根本不存在。在早期的C++ 11草案中,重载被声明为删除的评论(N3092×20.82.2):

    // deleted overloads close possible hole in the type system
    

    它没有说明“类型系统中可能存在的漏洞”是什么。在TR1和Boost中,重载是声明的,但没有定义。TR1规范注释(N1836§3.7.2.6):

    [ 类似布尔值的转换打开了一个漏洞,通过这个漏洞可以比较两个函数实例 == != . 这些未定义 void 尾注

    我对“漏洞”的理解是如果我们有 bool 转换函数,该转换可用于等式比较(以及在其他情况下):

    struct S {
        operator bool() { return false; }
    };
    
    int main() {
        S a, b;
        bool are_equal(a == b); // Uses operator bool on a and b!  Oh no!
    }
    

    我认为C++中的安全BoL习惯用法和C++ 11中的显式转换函数用于避免这个“漏洞”。 function 并且C++ 11使 转换函数显式。

    作为一个同时具有这两个属性的类的例子, std::shared_ptr 布尔 转换函数与等式具有可比性。

    std::函数 不可比性?“类型系统中可能存在的漏洞”是什么它和 ?

    8 回复  |  直到 4 年前
        1
  •  37
  •   Mike Seymour    10 年前

    为什么是 std::function 不可比性?

    std::函数

    “类型系统中可能存在的漏洞”是什么

    我想这意味着删除操作符要容易得多,而且肯定地知道使用它们永远不会给出有效的代码,而不是证明在一些以前未发现的角落情况下不可能发生不需要的隐式转换。

    它和 std::shared_ptr ?

    具有定义良好的相等语义;当且仅当两个指针都为空或都为非空且指向同一对象时,这两个指针才相等。

        2
  •  22
  •   Evan Teran    7 年前

    std::function 不幸的是,对象在一般意义上是不可解的。例如:

    #include <boost/bind.hpp>
    #include <boost/function.hpp>
    #include <cstdio>
    
    void f() {
        printf("hello\n");
    }
    
    int main() {
        boost::function<void()> f1 = f;
        boost::function<void()> f2 = boost::bind(f);
    
        f1();
        f2();
    }
    

    f1 f2 平等?如果我添加任意数量的函数对象,这些对象只是以各种方式相互包装,最终归结为对 f ... 还是平等的?

        3
  •  13
  •   Toby Speight    5 年前

    为什么是 std::function 不可比性?

    也就是说,执行比较的代码应该在可调用对象存储到 std::函数

    “通用多态函数包装器”


    重要的是要注意 compare a boost::function 使用可调用对象(但不使用其他对象) boost::函数 )

    == != 针对任何可以存储在包装器中的函数对象。

    这是可能的,因为执行这种比较的函数是基于已知的操作数类型在比较点实例化的。

    此外, 有一个 target template member function ,可用于执行类似的比较。事实上 boost::函数 的比较运算符是 implemented in terms of target member function

    function_comparable .


    • 即使这样,你也会得到一个狭隘的相等概念,因为等价函数会比较不相等,如果(例如)它们是由不同顺序的绑定参数构造的。我相信在一般情况下是不可能检验等价性的。

    • 我可能错了,但我认为平等是必要的 std::函数

    • 因为图灵机的等价性是不可判定的。给定两个不同的函数对象,您不可能确定它们是否计算相同的函数。[该答案已被删除]

    我完全不同意这一点:这不是我的工作 std::函数 进行比较;它的工作就是 重定向 请求与底层对象进行比较-仅此而已。

    std::函数 不需要推导比较算法。

    如果底层对象类型定义了比较,但工作不正确,或者有一些不寻常的语义-这不是比较的问题 std::函数 它本身也是,但这是 基础类型


    有可能实施 功能比较 基于 std::函数 .

    template<typename Callback,typename Function> inline
    bool func_compare(const Function &lhs,const Function &rhs)
    {
        typedef typename conditional
        <
            is_function<Callback>::value,
            typename add_pointer<Callback>::type,
            Callback
        >::type request_type;
    
        if (const request_type *lhs_internal = lhs.template target<request_type>())
            if (const request_type *rhs_internal = rhs.template target<request_type>())
                return *rhs_internal == *lhs_internal;
        return false;
    }
    
    #if USE_VARIADIC_TEMPLATES
        #define FUNC_SIG_TYPES typename ...Args
        #define FUNC_SIG_TYPES_PASS Args...
    #else
        #define FUNC_SIG_TYPES typename function_signature
        #define FUNC_SIG_TYPES_PASS function_signature
    #endif
    
    template<FUNC_SIG_TYPES>
    struct function_comparable: function<FUNC_SIG_TYPES_PASS>
    {
        typedef function<FUNC_SIG_TYPES_PASS> Function;
        bool (*type_holder)(const Function &,const Function &);
    public:
        function_comparable() {}
        template<typename Func> function_comparable(Func f)
            : Function(f), type_holder(func_compare<Func,Function>)
        {
        }
        template<typename Func> function_comparable &operator=(Func f)
        {
            Function::operator=(f);
            type_holder=func_compare<Func,Function>;
            return *this;
        }
        friend bool operator==(const Function &lhs,const function_comparable &rhs)
        {
            return rhs.type_holder(lhs,rhs);
        }
        friend bool operator==(const function_comparable &lhs,const Function &rhs)
        {
            return rhs==lhs;
        }
        friend void swap(function_comparable &lhs,function_comparable &rhs)// noexcept
        {
            lhs.swap(rhs);
            lhs.type_holder.swap(rhs.type_holder);
        }
    };
    

    有一处不错的房产- 可与 我也是。

    例如,假设我们有 vector of std::function s ,我们想给用户 register_callback unregister_callback 功能。使用 功能比较 取消注册\u回调

    void register_callback(std::function<function_signature> callback);
    void unregister_callback(function_comparable<function_signature> callback);
    

    Live demo at Ideone

    演示源代码:

    //             Copyright Evgeny Panasyuk 2012.
    // Distributed under the Boost Software License, Version 1.0.
    //    (See accompanying file LICENSE_1_0.txt or copy at
    //          http://www.boost.org/LICENSE_1_0.txt)
    
    #include <type_traits>
    #include <functional>
    #include <algorithm>
    #include <stdexcept>
    #include <iostream>
    #include <typeinfo>
    #include <utility>
    #include <ostream>
    #include <vector>
    #include <string>
    
    using namespace std;
    
    // _____________________________Implementation__________________________________________
    
    #define USE_VARIADIC_TEMPLATES 0
    
    template<typename Callback,typename Function> inline
    bool func_compare(const Function &lhs,const Function &rhs)
    {
        typedef typename conditional
        <
            is_function<Callback>::value,
            typename add_pointer<Callback>::type,
            Callback
        >::type request_type;
    
        if (const request_type *lhs_internal = lhs.template target<request_type>())
            if (const request_type *rhs_internal = rhs.template target<request_type>())
                return *rhs_internal == *lhs_internal;
        return false;
    }
    
    #if USE_VARIADIC_TEMPLATES
        #define FUNC_SIG_TYPES typename ...Args
        #define FUNC_SIG_TYPES_PASS Args...
    #else
        #define FUNC_SIG_TYPES typename function_signature
        #define FUNC_SIG_TYPES_PASS function_signature
    #endif
    
    template<FUNC_SIG_TYPES>
    struct function_comparable: function<FUNC_SIG_TYPES_PASS>
    {
        typedef function<FUNC_SIG_TYPES_PASS> Function;
        bool (*type_holder)(const Function &,const Function &);
    public:
        function_comparable() {}
        template<typename Func> function_comparable(Func f)
            : Function(f), type_holder(func_compare<Func,Function>)
        {
        }
        template<typename Func> function_comparable &operator=(Func f)
        {
            Function::operator=(f);
            type_holder=func_compare<Func,Function>;
            return *this;
        }
        friend bool operator==(const Function &lhs,const function_comparable &rhs)
        {
            return rhs.type_holder(lhs,rhs);
        }
        friend bool operator==(const function_comparable &lhs,const Function &rhs)
        {
            return rhs==lhs;
        }
        // ...
        friend void swap(function_comparable &lhs,function_comparable &rhs)// noexcept
        {
            lhs.swap(rhs);
            lhs.type_holder.swap(rhs.type_holder);
        }
    };
    
    // ________________________________Example______________________________________________
    
    typedef void (function_signature)();
    
    void func1()
    {
        cout << "func1" << endl;
    }
    
    void func3()
    {
        cout << "func3" << endl;
    }
    
    class func2
    {
        int data;
    public:
        explicit func2(int n) : data(n) {}
        friend bool operator==(const func2 &lhs,const func2 &rhs)
        {
            return lhs.data==rhs.data;
        }
        void operator()()
        {
            cout << "func2, data=" << data << endl;
        }
    };
    struct Caller
    {
        template<typename Func>
        void operator()(Func f)
        {
            f();
        }
    };
    class Callbacks
    {
        vector<function<function_signature>> v;
    public:
        void register_callback_comparator(function_comparable<function_signature> callback)
        {
            v.push_back(callback);
        }
        void register_callback(function<function_signature> callback)
        {
            v.push_back(callback);
        }
        void unregister_callback(function_comparable<function_signature> callback)
        {
            auto it=find(v.begin(),v.end(),callback);
            if(it!=v.end())
                v.erase(it);
            else
                throw runtime_error("not found");
        }
        void call_all()
        {
            for_each(v.begin(),v.end(),Caller());
            cout << string(16,'_') << endl;
        }
    };
    
    int main()
    {
        Callbacks cb;
        function_comparable<function_signature> f;
        f=func1;
        cb.register_callback_comparator(f);
    
        cb.register_callback(func2(1));
        cb.register_callback(func2(2));
        cb.register_callback(func3);
        cb.call_all();
    
        cb.unregister_callback(func2(2));
        cb.call_all();
        cb.unregister_callback(func1);
        cb.call_all();
    }
    

    输出为:

    func1
    func2, data=1
    func2, data=2
    func3
    ________________
    func1
    func2, data=1
    func3
    ________________
    func2, data=1
    func3
    ________________
    

    std::type_index ,可以实现类似于 std::less )甚至是散列。不仅在不同类型之间排序,而且在同一类型内排序(这需要类型的支持,比如 LessThanComparable ).

        4
  •  6
  •   Toby Speight    5 年前

    根据 http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#1240 :

    这里的主要评论是 历史 std::function ,其中 采用N1402。在那期间 时间无显式转换函数 还有“安全布尔”这个成语 (基于指向成员的指针)是 流行技术。唯一的 这个成语的缺点是 给定两个对象 f1 f2 类型 std::函数

    f1 == f2;
    

    只是因为 内置 operator== 成员是在一个 用户定义的转换。为了解决这个问题, 增加了比较函数,如 我更希望 最终导致联动错误。 删除的新语言工具 功能提供了更好的 诊断机制来解决这个问题 问题。

    安全布尔习语的显式转换 到 bool ,原来的“孔中型” 因此评论是错误的 也应该移除。

    std::函数 对象,这可能是因为它们可以保存全局/静态函数、成员函数、函子等,并且可以这样做 “擦除”有关基础类型的一些信息。因此,实现相等运算符可能是不可行的。

        5
  •  4
  •   Yola    9 年前

    实际上,你可以比较目标。它可能会起作用,这取决于你想从比较中得到什么。

    template <class Function>
    struct Comparator
    {
        bool operator()(const Function& f1, const Function& f2) const
        {
            auto ptr1 = f1.target<Function>();
            auto ptr2 = f2.target<Function>();
            return ptr1 < ptr2;
        }
    };
    
    typedef function<void(void)> Function;
    
    set<Function, Comparator<Function>> setOfFunc;
    
    void f11() {}
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        cout << "was inserted - " << setOfFunc.insert(bind(&f11)).second << endl;  // 1 - inserted
        cout << "was inserted - " << setOfFunc.insert(f11).second << endl;         // 0 - not inserted
        cout << "# of deleted is " << setOfFunc.erase(f11) << endl;
    
        return 0;
    }
    

    UPS,它仅在C++ 11之后才有效。

        6
  •  -1
  •   Mercyful    9 年前

    不如试试下面的方法,这对测试模板很有效。

    if (std::is_same<T1, T2>::value)
    {
        ...
    }
    
        7
  •  -2
  •   graphitemaster    13 年前