2013-01-23 11 views
6

mam napisane następującą implementację dla ogólnego systemu sygnał/gniazd:Idealne przekazywanie variading argumentów szablonu

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

Następnie testowałem moją klasę stosując następujące proste przypadki:

Signal<int> signal; 

// Case 1 
signal(0); 

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

Przypadek 1 kompiluje się bez problemu. Przypadek 2, z drugiej strony, generuje następujący błąd pod 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}]’ 

rozumiem sprawa ma coś wspólnego z idealnym-spedycji (i mojego niezrozumienia tego ostatniego). Zainspirowałem się jednak z implementacji std :: make_shared() & std :: make_tuple() i nie widzę żadnej różnicy w sposobie przekazywania argumentów variadic do delegatów. Jedyna zauważalna różnica polega na tym, że zarówno make_shared(), jak i make_tuple() są szablonami funkcyjnymi, a nie szablonem klasy, takim jak powyższa implementacja Signal.

- EDIT -

W odpowiedzi na różne komentarze, oto nowa wersja klasy realizacji sygnał, który nie cierpi z wyżej wymienionych zagadnień. Dodatkowo możliwe jest teraz rozłączenie delegatów za pomocą nieprzezroczystego tokena zwróconego przez funkcję łączenia. Wynik może nie być tak elastyczny i mocny jak inne implementacje (np. Boost :: signal), ale przynajmniej ma tę zaletę, że jest prosty i lekki.

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' to po prostu' int && args'. – Nawaz

+0

@Nawaz tak, brakowało mi tego, że wyraźnie określił to wyżej. Nieważne, co właśnie powiedziałem. Tak, perfekcyjne przekazywanie działa tylko wtedy, gdy pozwalasz wydedukować typ szablonu. –

+0

Do tego, co potrzebuje OP (lub _can_), należy mieć oddzielne 'Args ...' dla 'operator()'. –

Odpowiedz

5

Problemem jest to, że jesteś jawnie określając parametr szablonu jako int. Jak wspomniał Nawaz, Args&&... zostaje rozwinięty do int&& i nie można związać wartości l do wartości odniesienia rvalue.

Powodem idealny spedycja działa to, że podczas wywołania funkcji (na przykład) bez podania argumentów szablonu, dostają wywnioskować albo & lub && a następnie upadku odniesienia (czytaj o odniesienie zawaleniem, jeśli nie wiem co to jest). You do jawnie to określ, więc powstrzymujesz zwijanie referencji i wkręcasz wszystko.

Jedno można zrobić, to dać operator() swoją własną listę argumentów szablonu:

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

ten sposób można pozwolić odniesienia zawaleniem dbać o to dla ciebie.

+0

Jest to ten sam problem: teraz, jeśli deklaruję 's' jako' Signal s; ', a następnie nazywam go' int i = 10; s (i); 'to się nie skompiluje! – Nawaz

+0

@Nawaz następnie nie zadeklaruj 's' jako' Signam ' –

+0

Ale jaki jest sens? – Nawaz

Powiązane problemy