2010-09-02 70 views
58

此問題也適用於boost::functionstd::tr1::function爲什麼std :: 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 
} 

在C++ 11中,operator==operator!=重載就是不存在的。在早期的C++ 11草案,被宣佈重載與評論(N3092§20.8.14.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++ 03中的safe-bool習慣用法和C++ 11中顯式轉換函數的使用被用來避免這個「漏洞」。 Boost和TR1都使用function中的safe-bool成語,C++ 11使bool轉換函數顯式化。

作爲具有兩個的類的示例,std::shared_ptr都具有明確的bool轉換函數並且是相等的。

爲什麼std::function不平等可比?什麼是「類型系統中可能出現的漏洞?」與std::shared_ptr有什麼不同?

+0

請注意,您可以要求'* a.target < ftor_type >()== * b.target < ftor_type >()'是否指向平等可比函子。雖然這有點挑剔(底層對象不會被隱式轉換爲所請求的類型),但它確實指定了正在使用的比較語義。 – Potatoswatter 2012-02-05 09:14:23

回答

36

爲什麼std::function不等於可比?

std::function是任意調用類型的包裝,所以爲了實現平等比較可言,你就必須要求所有可調用的類型是平等的,comparible,放置負擔的功能實現目標的人。即使那樣,你也會得到一個狹義的平等概念,因爲如果(例如)它們是通過以不同的順序綁定參數來構造的,等價函數會比較不等。我認爲在一般情況下測試等同性是不可能的。

什麼是「類型系統中的可能的孔?」

我想這意味着它更容易刪除運營商,並肯定知道使用它們絕不會放棄合法的代碼,而不是證明有沒有在一些以前沒有發現的角落情況下出現不必要的隱式轉換的可能性,。

std::shared_ptr有什麼不同?

std::shared_ptr具有定義良好的相等語義;兩個指針在當且僅當它們都是空的時候是相等的,或者都是非空的並指向同一個對象。

+0

謝謝。我非常關注「類型系統漏洞」的評論,我沒有考慮語義。我會接受這個答案,因爲在我看來,它最清楚地回答了第一部分和第三部分,儘管第二部分最好的解釋是[在硅谷的答案](http://stackoverflow.com/questions/3629835/why-is- stdfunction而不是平等可比/ 3629961#3629961)。 – 2010-09-02 19:36:17

6

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

這裏的主導意見是 的std::function的歷史,這 與N1402推出一部分。在 時間內,沒有明確的轉換函數 存在,並且「安全 - 布爾」成語 (基於指向成員的指針)是一種流行的技術。這個成語的唯一 缺點是 給出了兩個對象f1和 類型爲std :: F2函數的表達式

f1 == f2;

是良好的,只是因爲 內置的==操作符的指向 的成員被認爲是在單一的 用戶定義的轉換之後。爲了解決這個問題, 增加了一套未定義的 比較函數的過載集合,例如 超負荷分辨率會優先選擇 那些以鏈接錯誤結尾的那些。 刪除 函數的新語言工具提供了一個更好的 診斷機制來解決這個 問題。

在C++ 0x中,刪除的函數在引入顯式轉換運算符時被認爲是多餘的,所以它們可能會在C++ 0x中被刪除。

這個問題的中心點, 有替代的 安全,布爾成語的通過顯式轉換 到bool原「中的 類型系統漏洞」也不再存在, 因此評論是錯誤的,並且 多餘的函數定義 也應該被刪除。

至於爲什麼你不能比較std::function對象,這可能是因爲他們都不可能持有全局/靜態函數,成員函數,仿函數等,要做到這一點std::function「擦除」有關基本類型的一些信息。實施平等運營商可能因此而不可行。

+0

對,但是這個(原來的帖子,自修:-P)沒有解決「爲什麼」,只有「什麼」。查看「爲什麼」的Boost.Function FAQ。 – 2010-09-02 18:19:09

+0

感謝您的鏈接。我應該檢查缺陷列表;我只是想,因爲它被指定在所有三個相同,它是故意的,而不是一個缺陷。 – 2010-09-02 19:31:04

24

這在Boost.Function FAQ中有詳細討論。 :-)

+0

我應該認爲會有一個討論這個問題的Boost FAQ。謝謝。 – 2010-09-02 19:31:41

20

我可能是錯的,但我認爲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(); 
} 

f1f2平等嗎?如果我添加任意數量的函數對象,它們會以各種方式相互包裝,最終歸結爲f ...仍然相等?

+5

+1對於一個簡單易用的例子來說,標準和常見問題解答很棒,但對於人們而言,它們往往有點過於抽象,以至於只能感受到原因。 – 2010-09-02 19:40:53

+0

同意@Matthieu:這是一個很好的,我沒有想到的問題的簡單例子。 – 2010-09-02 19:43:02

+0

boost :: function的作業(如果它支持比較) - 不是推導比較算法,而是重定向比較調用。如果底層對象沒有定義比較 - 那麼它就是編譯錯誤。如果底層對象定義了錯誤的比較 - 它也不是boost :: function的問題。 – 2012-10-28 00:02:26

-1

至少可以做到的是,如果std :: function保存用於綁定到一個字符串的函數的地址,並使用字符串比較來代替。

12

爲什麼std :: function不相等?

我認爲主要的原因是,如果它是,那麼它不能用於非平等可比類型,即使從不執行平等比較。

I.e.執行比較的代碼應該提前實例化 - 當可調用對象存儲到std :: function中時,例如在構造函數或賦值運算符中。

這樣的限制將大大縮小應用範圍,並且顯然不被「通用多態函數包裝器」所接受。


這是improtant要注意,有可能compare boost::function與調用對象(但不與其它的boost ::功能)

函數對象包裝可以通過==進行比較或!=針對任何可以存儲在包裝器中的函數對象。

這是可能的,因爲執行這種比較的函數是基於已知操作數類型在比較點處即時的。

此外,std :: functiontarget template member function,它可以用來執行類似的比較。實際上boost :: function的比較運算符是implemented in terms of target member function

因此,沒有技術障礙阻止實施function_comparable


其中的答案有共同的模式「一般是不可能的」:

  • 即使到那時,你會得到平等的狹義的概念,等效功能會比較,如果不相等(爲例如)它們是通過以不同順序綁定參數來構造的。我認爲在一般情況下測試等同性是不可能的。

  • 我可能是錯的,但我認爲,平等是性病::函數對象是可惜不是一般意義上的解。

  • 因爲圖靈機的等價性是不可判定的。給定兩個不同的函數對象,你不可能確定它們是否計算相同的函數。 [這個答案被刪除]

我完全不同意這種觀點:它不是的std ::功能的工作進行比較本身,它的工作就是重定向要求比較底層的對象 - 就這樣。

如果底層對象類型沒有定義比較 - 在​​任何情況下都是編譯錯誤,不需要使用std :: function來推導比較算法。

如果底層對象類型定義了比較,但是其中的工作不正確,或者有一些不尋常的語義 - 它不是std :: function本身的問題,但它是底層類型的問題。


有可能基於的std ::函數來實現function_comparable

這裏是證明了概念:

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

有一些漂亮的酒店 - function_comparable可以針對的std ::功能也進行比較。

舉例來說,假設我們有vector of std::function's,我們希望給用戶register_callbackunregister_callback功能。的function_comparable使用只爲unregister_callback參數要求:

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 
________________ 

P.S.看起來在std::type_index的幫助下,可以實現類似於類別function_comparable類別,其也支持排序(即較少)或甚至散列。但不僅是在不同類型之間進行排序,還要在同一類型中進行排序(這需要類型支持,如LessThanComparable)。

3

其實,你可以比較目標。它可能取決於你想要比較的東西。

這裏不平等的代碼,但你可以看到它是如何工作:

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有效。

+1

很好..解決了我的問題。 – ashish 2016-10-26 14:30:10

0

如何嘗試如下所示,這對測試模板很有效。

if (std::is_same<T1, T2>::value) 
{ 
    ... 
} 
相關問題