2013-01-23 37 views
6

我已經寫了下面的實現一個通用的信號/槽系統完美轉發:的variading模板參數

Signal<int> signal; 

// Case 1 
signal(0); 

//Case 2 
int i(0); 
signal(i); 

template< typename... Args > 
class Signal : NonCopyable 
{ 
public: 

    typedef std::function< void (Args...) > Delegate; 

    void connect(const Delegate& delegate); 
    void operator()(Args&&... args) const; 

private: 

    std::list<Delegate> _delegates; 
}; 

template< typename... Args > 
void Signal<Args...>::connect(const Delegate& delegate) 
{ 
    _delegates.push_front(delegate); 
} 

template< typename... Args > 
void Signal<Args...>::operator()(Args&&... args) const 
{ 
    for (const Delegate& delegate : _delegates) 
     delegate(std::forward<Args>(args)...); 
} 

後來,我用下面簡單的情況下測試了我的班案例1編譯沒有問題。個案2中,另一方面,會生成以下錯誤GCC 4.7.2下:

/home/pmjobin/Workspace/main.cpp:1196:10: error: cannot bind ‘int’ lvalue to ‘int&&’ 
/home/pmjobin/Workspace/main.cpp:82:6: error: initializing argument 1 of ‘void Signal<Args>::operator()(Args&& ...) const [with Args = {int}]’ 

我理解這個問題有事情做完美轉發(和我後面的誤解)。但是,我從std :: make_shared()& std :: make_tuple()實現中激發了我自己,並且我沒有看到將可變參數轉發給委託的方式有任何區別。唯一明顯的區別是make_shared()和make_tuple()都是函數模板,而不是像上面的Signal實現那樣的類模板。

- 編輯 -

在應對各種意見,這裏是信號類實現不從上述問題的影響的新版本。此外,現在可以使用connect函數返回的不透明令牌斷開委託。結果可能不像其他實現那樣靈活和強大(比如boost :: signal),但至少它具有簡單和輕量級的優點。

template< typename Signature > 
class Signal : NonCopyable 
{ 
public: 

    typedef std::function<Signature> Delegate; 

    class DisconnectionToken 
    { 
     DisconnectionToken(typename std::list<Delegate>::iterator it) 
      : _it(it) 
     {} 

     typename std::list<Delegate>::iterator _it; 

     friend class Signal; 
    }; 

    DisconnectionToken connect(const Delegate& delegate); 
    void disconnect(DisconnectionToken& token); 

    template< typename... Args > 
    void operator()(Args&&... args) const; 

private: 

    std::list<Delegate> _delegates; 
}; 

template< typename Signature > 
typename Signal<Signature>::DisconnectionToken Signal<Signature>::connect(const Delegate& delegate) 
{ 
    _delegates.push_front(delegate); 
    return DisconnectionToken(_delegates.begin()); 
} 

template< typename Signature > 
void Signal<Signature>::disconnect(DisconnectionToken& token) 
{ 
    if (token._it != _delegates.end()) 
    { 
     _delegates.erase(token._it); 
     token._it = _delegates.end(); 
    } 
} 

template< typename Signature > 
template< typename... Args > 
void Signal<Signature>::operator()(Args&&... args) const 
{ 
    for (const Delegate& delegate : _delegates) 
     delegate(std::forward<Args>(args)...); 
} 
+0

'Args && ... args'基本上是'int && args'。 – Nawaz

+0

@Nawaz是的,我錯過了他明確指出它上面。沒關係我剛纔說的。是的,完美的轉發主要只在您讓模板類型被推導時纔有效。 –

+0

OP需要(或_can_)做的是爲operator()提供單獨的'參數...'。 –

回答

5

問題是,您明確指定模板參數爲int。正如Nawaz提到的,Args&&...被擴展爲int&&,並且不能將左值綁定到右值引用。

之所以完美轉發的工作原理是,當你調用一個函數(例如)沒有指定模板參數,它們就推斷要麼&&&,然後引用崩潰(閱讀參考崩潰,如果你不知道那是什麼)。你雖然明確指定它,所以你禁止參考崩潰和擰緊它。

有一兩件事你可以做的是給予的模板參數的operator()它自己的名單:

template<typename... Args2> 
void operator()(Args2&&... args) const; 

... 

template< typename... Args > 
template< typename... Args2 > 
void Signal<Args...>::operator()(Args2&&... args) const 
{ 
    for (const Delegate& delegate : _delegates) 
     delegate(std::forward<Args2>(args)...); 
} 

這種方式可以讓參考倒塌照顧它給你。

+0

這是同樣的問題:現在,如果我聲明's'爲'Signal s;',然後將其稱爲int i = 10; s(i);'那麼它不會編譯! – Nawaz

+0

@Nawaz然後不宣佈's'作爲'Signam ' –

+0

但是那有什麼意義呢? – Nawaz