2012-04-03 21 views
9

Chương trình sau đây không xây dựng trong VS11 beta, gcc 4.5, hoặc kêu vang 3,1std :: chủ đề với động phi copyable luận,

#include <thread> 
#include <memory> 

int main() { 
    std::unique_ptr<int> p; 
    std::thread th([](std::unique_ptr<int>) { 

    },std::move(p)); 
    th.join(); 
} 

Điều này là do các loại đối số là không copyable, nhưng thực hiện các nỗ lực để sao chép nó.

Theo như tôi có thể nói, chương trình này được thiết lập tốt và sẽ hoạt động. Các yêu cầu cho std :: thread dường như ngụ ý rằng các đối số có thể di chuyển, không thể sao chép sẽ hoạt động ở đây. Cụ thể là nó nói rằng đối tượng có thể gọi và mỗi đối số phải thỏa mãn các yêu cầu MoveConstructible và rằng INVOKE(DECAY_COPY(std::forward<F>(f)),DECAY_COPY(std::forward<Args>(args))...) phải là một biểu thức hợp lệ.

Trong trường hợp này tôi nghĩ biểu hiện hoạt động ra vào một cái gì đó như:

template <class T> typename std::decay<T>::type decay_copy(T&& v) 
{ return std::forward<T>(v); } 

std::unique_ptr<int> p; 
auto f = [](std::unique_ptr<int>) {}; 

decay_copy(f)(decay_copy(std::move(p))); 

Và tôi không nghĩ rằng đây là nghĩa vụ liên quan đến một bản sao của p. gcc ít nhất có thể biên dịch biểu thức này, mặc dù VS11 thì không.

  1. Tôi có sai về yêu cầu và đối số phải có thể sao chép được không?
  2. Tiêu chuẩn có để lại bất kỳ sự cố nào về vấn đề này cho việc triển khai sao chép đối số không?
  3. Hoặc việc triển khai tôi đã thử không tuân thủ?
+0

Dường như bạn đang chuyển đối số chuỗi theo bản sao (theo chữ ký hàm ẩn danh). Không nên loại đối số là 'std :: unique_ptr &&' hoặc 'const std :: unique_ptr &'? –

+2

@ André: Không có thứ gì đi qua bản sao; chuyển đối số bằng _value_ sẽ sao chép hoặc di chuyển tùy thuộc vào việc người gọi có vượt qua một giá trị hay giá trị rvalue hay không. – ildjarn

+1

@ildjarn: xin lỗi, tôi có nghĩa là "theo giá trị", không phải "theo bản sao". Nó trượt tâm trí của tôi rằng đi qua các đối số theo giá trị sẽ chọn các nhà xây dựng di chuyển nếu có sẵn. –

Trả lời

14

Từ 30.3.1.2, khoản 3 và 4 của N3337:

template <class F, class ...Args> explicit thread(F&& f, Args&&... args);

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

Hiệu ứng: Xây dựng đối tượng của chuỗi loại. Chuỗi thực thi mới thực thi INVOKE (DECAY_-COPY (std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...) với các cuộc gọi đến DECAY_COPY đang được đánh giá trong chuỗi tạo. Bất kỳ giá trị trả về nào từ lời gọi này đều bị bỏ qua. [Lưu ý: Điều này ngụ ý rằng bất kỳ ngoại lệ nào không được ném từ việc yêu cầu bản sao của f sẽ được đưa vào chuỗi xây dựng chứ không phải chuỗi mới. —end note] Nếu lời gọi INVOKE (DECAY_COPY (std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...) chấm dứt với một ngoại lệ không bị bắt, std :: terminate sẽ được gọi.

Vì vậy, có, điều này sẽ hiệu quả. Nếu không, thì đó là lỗi trong quá trình triển khai của bạn.

Lưu ý rằng mọi chuyển động/sao chép thông số sẽ xảy ra trên chuỗi mới. Bạn đang chuyển tham chiếu đến một chuỗi khác, vì vậy bạn cần đảm bảo rằng chúng vẫn tồn tại cho đến khi chuỗi đó bắt đầu.

+2

Và nó hoạt động với g ++, kể từ phiên bản 4.7 – je4d

+0

Và bây giờ tôi không còn có thể tạo lại lỗi trong clang nữa, mặc dù mã tôi đã sử dụng trước đó nằm trong kho lưu trữ nguồn và tôi có dòng lệnh chính xác trong lịch sử của mình. Tôi đoán tôi nên kiểm tra lại vs11. – bames53

+1

Thực ra có vẻ như vấn đề là phiên bản cũ của libC++ so với phiên bản mới nhất. – bames53

3

Là một thay thế, và như là tiêu chuẩn std::thread thành ngữ, bạn có thể vượt qua một wrapper tham khảo:

int p; 
std::thread([](int & x) { /* ... */ }, std::ref(p)); 

Điều này tạo ra một đối tượng kiểu std::reference_wrapper<int>, trong đó có ngữ nghĩa giá trị và kết thúc tốt đẹp một tham chiếu đến một int (tức sao chép bí danh của trình bao bọc tham chiếu).

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