2013-01-23 25 views
6

Tôi đã viết thực hiện sau đây cho một hệ thống tín hiệu/khe chung:Perfect chuyển tiếp của variading mẫu đối số

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

Sau đó, tôi đã kiểm tra lớp học của tôi bằng cách sử dụng các trường hợp đơn giản sau đây:

Signal<int> signal; 

// Case 1 
signal(0); 

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

Trường hợp 1 biên dịch mà không có vấn đề. Trường hợp 2, mặt khác, tạo ra lỗi sau theo 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}]’ 

Tôi hiểu vấn đề có liên quan đến việc chuyển tiếp hoàn hảo (và sự hiểu lầm của tôi về sau). Tuy nhiên, tôi đã lấy cảm hứng từ std :: make_shared() & std :: make_tuple() triển khai và tôi không thấy bất kỳ sự khác biệt trong cách tôi chuyển tiếp các đối số variadic cho các đại biểu. Điểm khác biệt đáng chú ý nhất là make_shared() và make_tuple() là các khuôn mẫu hàm chứ không phải là một mẫu lớp như thực hiện Signal ở trên.

- EDIT -

Để đối phó với những ý kiến ​​khác nhau, đây là một phiên bản mới của việc thực hiện lớp tín hiệu mà không bị các vấn đề nói trên. Ngoài ra, bây giờ có thể ngắt kết nối các đại biểu bằng mã thông báo mờ được trả về bởi hàm kết nối. Kết quả có thể không linh hoạt và mạnh mẽ như các triển khai khác ngoài đó (chẳng hạn như boost :: signal), nhưng ít nhất, nó có lợi ích là đơn giản và nhẹ.

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' về cơ bản là' int && args'. – Nawaz

+0

@Nawaz yeah, tôi đã bỏ lỡ rằng anh ta đã chỉ định rõ ràng nó ở trên. Không bao giờ nói những gì tôi vừa nói. Vâng, chuyển tiếp hoàn hảo chủ yếu chỉ hoạt động khi bạn cho phép loại mẫu được suy luận. –

+0

Những gì OP cần (hoặc _can_) làm là có một 'Args ...' riêng biệt cho toán tử '()'. –

Trả lời

5

Vấn đề là bạn chỉ định rõ ràng thông số mẫu làm int. Như Nawaz đã đề cập, Args&&... được mở rộng thành int&& và bạn không thể liên kết giá trị với tham chiếu rvalue. Lý do các công việc chuyển tiếp hoàn hảo là khi bạn gọi một hàm (ví dụ) mà không cần chỉ rõ các đối số mẫu, chúng được suy ra hoặc & hoặc && và sau đó thu gọn tham chiếu (đọc về tham chiếu sụp đổ nếu bạn không biết cái đó là gì). Bạn làm chỉ định rõ ràng nó mặc dù, vì vậy bạn ngăn chặn tham chiếu sụp đổ và vít tất cả lên.

Một điều bạn có thể làm là cung cấp cho các operator() danh sách riêng của nó đối số mẫu:

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

Bằng cách này bạn có thể cho tài liệu tham khảo sụp đổ nâng niu mang về điều đó cho bạn.

+0

Nó là cùng một vấn đề: bây giờ nếu tôi khai báo 's' là' Signal s; 'và sau đó gọi nó là' int i = 10; s (i); 'thì nó sẽ không biên dịch! – Nawaz

+0

@Nawaz sau đó không khai báo 's' là' Signam ' –

+0

Nhưng sau đó là điểm gì? – Nawaz

Các vấn đề liên quan