簡體   English   中英

完美轉發變量模板參數

[英]Perfect forwarding of variading template arguments

我已經為通用信號/插槽系統編寫了以下實現:

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

之后,我使用以下簡單案例測試了我的課程:

Signal< int > signal;

// Case 1
signal(0);

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

案例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實現。

- 編輯 -

響應各種評論,這里是Signal類實現的新版本,它沒有遇到上述問題。 此外,現在可以使用connect函數返回的opaque標記斷開委托。 結果可能不像其他實現那樣靈活和強大(例如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)...);
}

問題是你明確地將模板參數指定為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)...);
}

通過這種方式,您可以讓參考折疊為您處理。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM