che ho scritto la seguente implementazione per un generico sistema di segnale/Slot:perfetta inoltro di argomenti di template variading
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)...);
}
In seguito, ho provato la mia classe utilizzando i seguenti casi semplici:
Signal<int> signal;
// Case 1
signal(0);
//Case 2
int i(0);
signal(i);
Il caso 1 viene compilato senza problemi. Caso 2, d'altra parte, genera il seguente errore in 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}]’
capisco il problema ha a che fare con perfetta-forwarding (e la mia incomprensione di quest'ultimo). Tuttavia, mi sono ispirato alle implementazioni std :: make_shared() & std :: make_tuple() e non vedo alcuna differenza nel modo in cui inoltro gli argomenti variadici ai delegati. L'unica differenza degna di nota è che sia make_shared() che make_tuple() sono template di funzioni piuttosto che un modello di classe come l'implementazione Signal sopra.
- EDIT -
In risposta ai vari commenti, qui è una nuova versione della implementazione della classe del segnale, che non soffre dei problemi di cui sopra. Inoltre, è ora possibile disconnettere i delegati usando un token opaco restituito dalla funzione connect. Il risultato potrebbe non essere flessibile e potente come altre implementazioni là fuori (come boost :: signal), ma almeno, ha il vantaggio di essere semplice e leggero.
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' è fondamentalmente' int && args'. – Nawaz
@Nawaz si, ho notato che l'ha esplicitamente specificato sopra. Non importa quello che ho appena detto. Sì, l'inoltro perfetto funziona principalmente quando si deduce il tipo di modello. –
Ciò che l'OP deve (o _can_) fare è avere un 'Args ...' separato per 'operator()'. –