2015-10-18 18 views
5

Tôi đang tạo hàng đợi công việc. Công việc sẽ được tạo trong luồng A, sau đó công việc sẽ được gửi đến luồng B và chuỗi B sẽ thực hiện công việc. Sau khi công việc đã được thực hiện, công việc sẽ được gửi lại cho chủ đề A.Quá nhiều bản sao khi liên kết các đối số mẫu variadic

#include <functional> 
#include <iostream> 
#include <memory> 

using namespace std; 

template<typename T, typename... Args> 
class Job 
{ 
    public: 
     Job(std::weak_ptr<T> &&wp, std::function<void(const Args&...)> &&cb) 
      : _cb(std::move(cb)), 
       _cbWithArgs(), 
       _owner(std::move(wp)) {} 

    public: 
     template<typename... RfTs> 
     void bind(RfTs&&... args) 
     { 
      // bind will copy args for three times. 
      _cbWithArgs = std::bind(_cb, std::forward<RfTs>(args)...); 
     } 

     void fire() 
     { 
      auto sp = _owner.lock(); 
      if (sp) 
      { 
       _cbWithArgs(); 
      } 
     } 

    private: 
     std::function<void(const Args& ...)> _cb; 
     std::function<void()> _cbWithArgs; 
     std::weak_ptr<T> _owner; 
}; 

struct Args 
{ 
    Args() = default; 
    Args(const Args &args) 
    { 
     cout << "Copied" << endl; 
    } 
}; 

struct Foo 
{ 
    void show(const Args &) 
    { 
     cout << "Foo" << endl; 
    } 
}; 

int main() 
{ 
    using namespace std::placeholders; 

    shared_ptr<Foo> sf (new Foo()); 
    Args args; 

    // Let's say here thread A created the job. 
    Job<Foo, Args> job(sf, std::bind(&Foo::show, sf.get(), _1)); 

    // Here thread B has finished the job and bind the result to the 
    // job. 
    job.bind(args); 

    // Here, thread A will check the result. 
    job.fire(); 
} 

Các mã trên biên dịch và hoạt động. Nhưng nó cho kết quả sau (g ++ 4.8.4 & clang có cùng kết quả):

Copied 
Copied 
Copied 
Foo 

Có ba bản sao! Không chấp nhận được, tôi không biết mình đã làm sai ở đâu. Tại sao ba bản sao? Tôi googled và tìm thấy một phương pháp từ đây: https://stackoverflow.com/a/16868401, nó chỉ sao chép params cho một thời gian. Nhưng nó phải khởi tạo hàm bind trong constructor.

Cảm ơn, Piotr Skotnicki. Thật không may, tôi không có trình biên dịch C++ 14. Vì vậy, chúng ta hãy làm cho args di chuyển:

struct Args 
{ 
    Args() = default; 
    Args(const Args &args) 
    { 
     cout << "Copied" << endl; 
    } 
    Args(Args &&) = default; 
    Args& operator=(Args &&) = default; 
}; 

Bây giờ, nó copys chỉ một lần :)

Cuối cùng, tôi đã thông qua mã từ chủ đề này https://stackoverflow.com/a/16868151/5459549. Mẫu gen_seq là ART đúng, tôi phải nói.

Trả lời

5

Bản sao đầu tiên được thực hiện bởi các std::bind bản thân:

std::bind(_cb, std::forward<RfTs>(args)...) 

như nó cần phải lưu trữ bị hư hỏng-bản sao của đối số của nó.

Hai bản sao khác được thực hiện bởi sự phân công để _cbWithArgs đó là loại std::function (§ 20.8.11.2.1 [func.wrap.func.con]):

template<class F> function& operator=(F&& f); 

Effects:function(std::forward<F>(f)).swap(*this);

nơi:

template<class F> function(F f); 

[...] *this mục tiêu một bản sao của f khởi tạo với std::move(f).

Tức là, một bản sao cho tham số của một hàm tạo và một bản sao khác để lưu trữ đối số f.

Kể từ Args là một loại không thể di chuyển, một nỗ lực để di chuyển từ kết quả của một cuộc gọi đến std::bind rơi trở lại một bản sao.

Có vẻ như không hợp lý trong mã của bạn để lưu trữ kết quả thực hiện công việc trong một std::function.Thay vào đó, bạn có thể lưu trữ những giá trị trong một tuple:

template <typename T, typename... Args> 
class Job 
{ 
public: 
    Job(std::weak_ptr<T> wp, std::function<void(const Args&...)> &&cb) 
     : _cb(std::move(cb)), 
      _owner(wp) {} 

public: 
    template<typename... RfTs> 
    void bind(RfTs&&... args) 
    { 
     _args = std::forward_as_tuple(std::forward<RfTs>(args)...); 
    } 

    void fire() 
    { 
     auto sp = _owner.lock(); 
     if (sp) 
     { 
      apply(std::index_sequence_for<Args...>{}); 
     } 
    } 

private:  
    template <std::size_t... Is> 
    void apply(std::index_sequence<Is...>) 
    { 
     _cb(std::get<Is>(_args)...); 
    } 

    std::function<void(const Args&...)> _cb; 
    std::weak_ptr<T> _owner; 
    std::tuple<Args...> _args; 
}; 

DEMO

+0

tiếc là tôi không biên dịch trong VC14, std :: tuple cần phải khởi tạo – MORTAL

+0

@MORTAL * "tiếc là tôi không biên dịch trong VC14, std :: tuple cần khởi tạo "*, bạn có thể xây dựng? –

+0

cảm ơn cho câu trả lời thông tin, tôi chạy mã của bạn trong VC14 nó không biên dịch, nó cho lỗi trở lại như 'tuple (67): lỗi C2476: 'constexpr' constructor không khởi tạo tất cả các thành viên'. tôi không biết nếu điều này có thể là một lỗi với VC14 – MORTAL

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