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)...);
}
'Args && ... args' to po prostu' int && args'. – Nawaz
@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. –
Do tego, co potrzebuje OP (lub _can_), należy mieć oddzielne 'Args ...' dla 'operator()'. –