2012-12-11 35 views
11

Tôi có một mẫu chức năng, nơi tôi muốn thực hiện chuyển tiếp hoàn hảo vào một lambda mà tôi chạy trên một chuỗi khác. Dưới đây là một trường hợp thử nghiệm tối thiểu mà bạn có thể trực tiếp biên soạn:Hoàn hảo Chuyển tiếp đến async lambda

#include <thread> 
#include <future> 
#include <utility> 
#include <iostream> 
#include <vector> 

/** 
* Function template that does perfect forwarding to a lambda inside an 
* async call (or at least tries to). I want both instantiations of the 
* function to work (one for lvalue references T&, and rvalue reference T&&). 
* However, I cannot get the code to compile when calling it with an lvalue. 
* See main() below. 
*/ 
template <typename T> 
std::string accessValueAsync(T&& obj) 
{ 

    std::future<std::string> fut = 
     std::async(std::launch::async, 
      [](T&& vec) mutable 
      { 
       return vec[0]; 
      }, 
      std::forward<T>(obj)); 

    return fut.get(); 
} 

int main(int argc, char const *argv[]) 
{ 
    std::vector<std::string> lvalue{"Testing"}; 

    // calling with what I assume is an lvalue reference does NOT compile 
    std::cout << accessValueAsync(lvalue) << std::endl; 

    // calling with rvalue reference compiles 
    std::cout << accessValueAsync(std::move(lvalue)) << std::endl; 

    // I want both to compile. 

    return 0; 
} 

Đối với trường hợp không biên soạn, đây là dòng cuối cùng của thông báo lỗi đó là dễ hiểu:

main.cpp|13 col 29| note: no known conversion for argument 1 from ‘std::vector<std::basic_string<char> >’ to ‘std::vector<std::basic_string<char> >&’ 

Tôi có một cảm giác nó có thể có một cái gì đó để làm với cách T&& được suy luận, nhưng tôi không thể xác định chính xác điểm thất bại và sửa chữa nó. Bất kỳ đề xuất?

Cảm ơn bạn!

EDIT: Tôi đang sử dụng gcc 4.7.0 chỉ trong trường hợp này có thể là một vấn đề biên dịch (có thể là không)

+0

Tôi không chắc liệu mình có đúng hay không, nhưng có thể C++ yêu cầu bạn * rõ ràng * sử dụng 'std :: move' trên các đối tượng có cả bản sao và di chuyển ngữ nghĩa. –

+0

Xin lỗi, tôi đã không cụm từ câu hỏi này. Hãy để tôi chỉnh sửa. Tóm lại, là tôi muốn cả hai hàm instantiations hoạt động, và chúng thực hiện một điều khác nhau (một chuyển tiếp vector bằng cách truyền tham chiếu xung quanh, chuyển tiếp khác bằng cách di chuyển tham chiếu rvalue mỗi lần). –

+0

Chỉ ra rằng nó hoạt động nếu bạn đúc thành '(const std :: vector &)'. –

Trả lời

7

Con đường tôi hiểu nó, bạn không thể sử dụng một chức năng thông qua async rằng hy vọng tài liệu tham khảo giá trị trái không const như đối số, bởi vì async sẽ luôn tạo bản sao của chúng trong nội bộ (hoặc di chuyển chúng bên trong) để đảm bảo chúng tồn tại và hợp lệ trong suốt thời gian chạy của chuỗi được tạo.

Cụ thể, chỉ số Standard nói về async(launch policy, F&& f, Args&&... args):

(§30.6.8)

(2) Yêu cầu: F và mỗi Ti trong Args phải đáp ứng các yêu cầu MoveConstructible. INVOKE(DECAY_COPY (std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...) (20.8.2, 30.3.1.2) phải là một biểu thức hợp lệ.

(3) Hiệu ứng: [...] nếu chính sách & khởi chạy :: async không khác - gọi INVOKE(DECAY_COPY (std::forward<F>(f)),DECAY_COPY (std::forward<Args>(args))...) (20.8.2, 30.3.1.2) như thể trong một chuỗi mới thực hiện được đại diện bởi đối tượng chuỗi với các cuộc gọi đến DECAY_COPY() đang được đánh giá trong chuỗi được gọi là không đồng bộ. Bất kỳ giá trị trả về nào được lưu trữ dưới dạng kết quả trong trạng thái được chia sẻ. Bất kỳ ngoại lệ nào được truyền từ việc thực hiện INVOKE (DECAY_COPY (std :: forward (f)), DECAY_COPY (std :: forward (args)) ...) được lưu trữ dưới dạng kết quả ngoại lệ trong trạng thái chia sẻ.
Đối tượng chuỗi được lưu trữ trong trạng thái chia sẻ và ảnh hưởng đến hành vi của bất kỳ đối tượng trả về không đồng bộ nào tham chiếu trạng thái đó.

Thật không may, điều này có nghĩa là bạn thậm chí không thể thay thế tham chiếu bằng std::reference_wrapper, bởi vì sau này không thể chuyển động. Tôi giả sử sử dụng một std::unique_ptr thay vì tham chiếu sẽ hoạt động (ngụ ý, tuy nhiên, đối số hàm của bạn sẽ luôn luôn sống trên heap).

(EDIT/SỬA CHỮA)
tôi đã làm việc trên một vấn đề liên quan khi tôi nhận ra rằng std::reference_wrapper thực sự cho phép khắc phục, mặc dù tôi khẳng định điều ngược lại ở trên.

Nếu bạn xác định hàm kết thúc tham chiếu giá trị trong một std::reference_wrapper, nhưng để nguyên tham chiếu rvalue không đổi, bạn có thể chuyển đối số T&& thông qua hàm này trước khi giao cho std::async.Tôi đã gọi chức năng này đặc biệt wrapper wrap_lval dưới đây:

#include <thread> 
#include <future> 
#include <utility> 
#include <iostream> 
#include <vector> 
#include <type_traits> 

/* First the two definitions of wrap_lval (one for rvalue references, 
    the other for lvalue references). */ 

template <typename T> 
constexpr T&& 
wrap_lval(typename std::remove_reference<T>::type &&obj) noexcept 
{ return static_cast<T&&>(obj); } 

template <typename T> 
constexpr std::reference_wrapper<typename std::remove_reference<T>::type> 
wrap_lval(typename std::remove_reference<T>::type &obj) noexcept 
{ return std::ref(obj); } 


/* The following is your code, except for one change. */ 
template <typename T> 
std::string accessValueAsync(T&& obj) 
{ 

    std::future<std::string> fut = 
    std::async(std::launch::async, 
      [](T&& vec) mutable 
      { 
      return vec[0]; 
      }, 
      wrap_lval<T>(std::forward<T>(obj))); // <== Passing obj through wrap_lval 

    return fut.get(); 
} 

int main(int argc, char const *argv[]) 
{ 
    std::vector<std::string> lvalue{"Testing"}; 

    std::cout << accessValueAsync(lvalue) << std::endl; 

    std::cout << accessValueAsync(std::move(lvalue)) << std::endl; 

    return 0; 
} 

Với sự thay đổi này, cả hai cuộc gọi đến accessValueAsync biên dịch và làm việc. Mục đầu tiên, sử dụng tham chiếu lvalue, tự động kết thúc nó trong một std::reference_wrapper. Sau này được tự động chuyển đổi trở lại tham chiếu lvalue khi std::async gọi hàm lambda.

+0

Cảm ơn bạn, có ý nghĩa. Tôi đoán bản sao phân rã chắc chắn rằng các tài liệu tham khảo không vượt qua được và sao chép chúng "phòng thủ". –

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