10

最近我發現shared_ptr沒有指向成員操作符->*的指針。我創建了一個簡單的例子:關於shared_ptr和指向成員操作符` - > *`和`std :: bind`的指針

template <typename Pointer, typename Function, typename... Args> 
auto invoke1(Pointer p, Function f, Args... args) -> decltype((p->*f)(args...)) 
{ 
    return (p->*f)(args...); 
} 
struct A { 
    void g() { std::cout << "A::g()\n"; } 
}; 
int main() { 
    A a; 
    invoke1(&a, &A::g); // works!! 
    std::shared_ptr<A> sa = std::make_shared<A>(); 
    invoke1(sa, &A::g); // compile error!! 
} 

Q1:爲什麼會這樣?爲什麼shared_ptr沒有這個操作符?

我加了這樣的運營商shared_ptr和例子開始工作:

template <typename T, typename Result> 
auto operator ->* (std::shared_ptr<T> pointer, Result (T::*function)()) ->decltype(std::bind(function, pointer)) 
{ 
    return std::bind(function, pointer); 
} 
template <typename T, typename Result, typename Arg1> 
auto operator ->* (std::shared_ptr<T> pointer, Result (T::*function)(Arg1 arg1)) ->decltype(std::bind(function, pointer, std::placeholders::_1)) 
{ 
    return std::bind(function, pointer, std::placeholders::_1); 
} 

Q2:這是該運營商正確實施?有沒有什麼地方有任何「黃金」規則來實施這樣的操作員,可能是我重新發明了輪子,或者完全走錯了方向,你怎麼看?有沒有辦法有一個單獨的功能有佔位性病執行這一操作,而不是儘可能多的功能...

之後,我來到了結論,std::bind可以在我的invoke方法可以使用。

template <typename Pointer, typename Function, typename... Args> 
auto invoke2(Pointer p, Function f, Args... args) 
        -> decltype(std::bind(f, p, args...)()) 
{ 
    return std::bind(f, p, args...)(); 
} 

這樣我的例子也工作沒有必要添加operator ->*shared_ptr

Q3:那麼,是std::bind現在視爲operator->*更換?

+0

你試過了嗎:invoke1(sa。得到(),&A::g); – Alexis

+0

@Alexis - 是的,我已經嘗試過,當然它的工作原理,但我認爲這個解決方法對於我的問題並不重要。 – PiotrNycz

+0

我甚至不知道你可以重載那個操作符。 – Mehrdad

回答

6

在堅果殼:是的std :: bind是成員函數指針的替代品。

爲什麼? 因爲成員函數指針是可怕的,而他們唯一的目的就是實現委託,這就是爲什麼的std ::綁定和std ::功能做

有關成員函數指針是如何實現的參考,請參閱我以前的答案here 。用最簡單的術語來說,成員函數指針被標準削弱了,因爲它們在轉換後不允許調用;這使得它們對於人們從成員函數指針所需要的行爲中的90%的行爲完全沒有意義:代表。

因此,std :: function用於表示抽象的「可調用」類型,用std :: bind將其綁定到成員函數指針。你絕對不應該混淆成員函數指針,而應該使用std :: bind和std :: function。

+1

你能否以某種方式解釋「投下後不允許打電話」,舉個例子?我通讀了你的鏈接答案 - 但我確實錯過了解釋。無論如何,+ 1x2爲你的答案。 – PiotrNycz

+1

@PiotrNycz使用成員函數指針作爲委託所需的一個關鍵特性是能夠在類之間進行轉換,例如從派生類到基類。雖然鑄造成員函數指針是合法的,但調用之前鑄造的一個不是。這是未定義的行爲。這意味着,例如,您不能存儲成員函數指針列表並以多態方式調用它們。儘管這在幾乎所有的編譯器中都可行,但它不是標準的,因此不是C++。 – Alice

2

我認爲shared_ptr沒有運算符->*,因爲無法爲任意數量的參數實現它(C++ 11允許爲其他用例執行)。另外,您可以輕鬆地爲調用get()的智能指針添加invoke函數的重載,因此使界面複雜化並不理想。

+0

這是不可能的任意數量的參數,但可以說,最多30個參數(我的gcc編譯器中有29個佔位符)。所以實際上,具有更多論據的功能在現實世界中不存在。第二件事是我知道有解決方法,@Alexis已經提到過這個。對於任何數量的參數都無法完成的觀察+1。但我仍然想知道,也許還有其他原因,並且仍然想要解答其他子問題。 – PiotrNycz

4

相信最簡單soultion將替換「結構解除引用」(->)運算符具有一對反引用(*)的和結構的參考(.)運算符:

template <typename Pointer, typename Function, typename... Args> 
auto invoke1(Pointer p, Function f, Args... args) -> decltype(((*p).*f)(args...)) 
{ 
    return ((*p).*f)(args...); 
} 
+0

這是如何定義_INVOKE_,它規定'std :: bind'如何工作 –

+0

好主意,謝謝。 – PiotrNycz

+1

'。*'是一個運算符,而不是兩個。此外,' - > *'與' - >'不一樣。 –