2013-06-03 40 views
12

Code:Move thế hệ chức năng thành viên

#include <iostream> 
#include <ios> 
#include <string> 
#include <type_traits> 
#include <memory> 

struct value 
{ 
    ~value() = default; 
    std::unique_ptr<std::string> s; 
}; 

int main() 
{ 
    std::cout << std::boolalpha; 
    std::cout << std::is_move_constructible<value>::value << '\n'; 
    std::cout << std::is_move_assignable<value>::value << '\n'; 

    using str_ptr = std::unique_ptr<std::string>; 
    std::cout << std::is_move_constructible<str_ptr>::value << '\n'; 
    std::cout << std::is_move_assignable<str_ptr>::value << '\n'; 

    return 0; 
} 

Output (biên soạn với g ++ v4.7.2, http://ideone.com/CkW1tG):

 
false 
false 
true 
true 

Như tôi đã mong đợi, value không được di chuyển constructible và không di chuyển nhượng vì :

~value() = default; 

do người dùng khai báo destructor, ngăn chặn thế hệ ngầm của các thành viên di chuyển theo phần 12.8 (xem bên dưới). Nếu destructor được lấy ra thì value sẽ chuyển động và có thể chuyển nhượng được, như tôi mong đợi (http://ideone.com/VcR2eq).

Tuy nhiên, khi định nghĩa của value được thay đổi thành (http://ideone.com/M8LHEA):

struct value 
{ 
    ~value() = default; 
    std::string s;  // std::unique_ptr<> removed 
}; 

đầu ra là:

 
true 
true 
true 
true 

value được bất ngờ di chuyển constructible và di chuyển nhượng. Tôi có hiểu nhầm hay đây là lỗi trình biên dịch?


nền: Tôi cung cấp một câu trả lời cho câu hỏi this và đã được thông báo rằng Tree<> là di chuyển, nhưng tôi không chắc chắn và đang cố gắng để xác định chắc chắn nếu nó là hay không.


Mục 8.4.2 chức năng Rõ ràng-defaulted của tiêu chuẩn C++ 11 (dự thảo n3337):

chức năng Rõ ràng-defaulted và chức năng ngầm-tuyên bố đang gọi chung là chức năng defaulted và việc triển khai sẽ cung cấp các định nghĩa ngầm định cho chúng (12.1 12.4, 12.8), có nghĩa là xác định chúng là đã bị xóa. Một chức năng thành viên đặc biệt được người dùng cung cấp nếu nó được người dùng khai báo và không được mặc định hoặc xóa một cách rõ ràng trên khai báo đầu tiên của nó. Chức năng mặc định do người dùng cung cấp (tức là, được mặc định rõ ràng sau lần khai báo đầu tiên) được xác định tại thời điểm được đặt mặc định rõ ràng; nếu một hàm như vậy được định nghĩa ngầm định là đã bị xóa, chương trình không đúng định dạng. [Ghi chú: Khai báo một chức năng như mặc định sau khi khai đầu tiên của nó có thể cung cấp thực hiện hiệu quả và định nghĩa ngắn gọn trong khi cho phép một giao diện nhị phân ổn định để một mã tiến hóa base.-end note]

Mục 12.8 Sao chép và di chuyển các đối tượng lớp (điểm 9):

 
If the definition of a class X does not explicitly declare a move constructor, 
one will be implicitly declared as defaulted if and only if 
- X does not have a user-declared copy constructor, 
- X does not have a user-declared copy assignment operator, 
- X does not have a user-declared move assignment operator, 
- X does not have a user-declared destructor, and 
- the move constructor would not be implicitly defined as deleted. 

Trả lời

9

std::is_move_constructible<T> là đúng khi và chỉ khi std::is_constructible<T, T&&> là đúng, nhưng điều đó không có nghĩa là một công trình như vậy sẽ gọi một constructor di chuyển, duy nhất mà người ta có thể xây dựng loại từ một rvalue cùng loại. Việc xây dựng như vậy có thể sử dụng một hàm tạo bản sao.

Khi value::sunique_ptr trình tạo bản sao của loại và toán tử gán bản sao được xác định là đã xóa, vì thành viên s không thể sao chép được. Nó không có một hàm tạo di chuyển và di chuyển toán tử gán bởi vì, như bạn đã chỉ ra, nó có một destructor do người dùng khai báo. Điều đó có nghĩa là nó không có hàm tạo bản sao và không có hàm khởi tạo (và không có các hàm tạo khác do người dùng định nghĩa có thể chấp nhận một đối số kiểu value&&) vì vậy std::is_constructible<value, value&&> là sai.

Khi value::s là một string constructor sao chép của loại và sao chép toán tử gán là không định nghĩa là xóa, vì s viên copyable, vv value cũng là copyable, và một loại CopyConstructible cũng là MoveConstructible, bởi vì đó là hợp lệ trong bối cảnh này:

value v1; 
value v2 = std::move(v1); // calls copy constructor 

Điều đó có nghĩa std::is_constructible<value, value&&> là đúng, mặc dù nó gọi các nhà xây dựng bản sao không phải là một nhà xây dựng di chuyển.

+0

+1 Cảm ơn, giải thích rất rõ ràng. Trong sự tò mò, bạn có biết về bất kỳ đặc điểm nào kiểm tra một cách rõ ràng cho sự hiện diện của các thành viên di chuyển không? – hmjd

+1

Không có. [_ "ngữ nghĩa ngôn ngữ cốt lõi không thực sự hữu ích đối với người viết thư viện, họ không quan tâm một lớp có thứ gì đó mà ngôn ngữ cốt lõi xem xét một nhà xây dựng bản sao hay không. Họ muốn biết nếu thực sự viết T (someT) sẽ hoạt động, cho dù nó gọi là một hàm tạo bản sao hoặc một hàm tạo di chuyển hoặc một hàm tạo thành viên hay bất kỳ hàm tạo nào. "_] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/ n3142.html) Tuy nhiên, bạn có thể kiểm tra 'is_move_constructible :: value &&! is_copy_constructible :: giá trị' –

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