2013-04-15 39 views
9

Tôi đang sử dụng Visual Studio 2012 Update 2 và đang gặp sự cố khi cố gắng hiểu tại sao std :: vector đang cố gắng sử dụng hàm tạo bản sao của unique_ptr. Tôi đã xem xét các vấn đề tương tự và hầu hết đều liên quan đến việc không có một hàm tạo và/hoặc toán tử di chuyển rõ ràng.Di chuyển ngữ nghĩa với unique_ptr

Nếu tôi thay đổi biến thành viên thành chuỗi, tôi có thể xác minh rằng hàm tạo di chuyển được gọi; tuy nhiên, cố gắng sử dụng kết quả unique_ptr trong lỗi biên dịch:

error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>'.

Tôi hy vọng một người nào đó có thể chỉ cho tôi những gì tôi đang bỏ lỡ, cảm ơn!

#include <vector> 
#include <string> 
#include <memory> 

class MyObject 
{ 
public: 
    MyObject() : ptr(std::unique_ptr<int>(new int)) 
    { 
    } 

    MyObject(MyObject&& other) : ptr(std::move(other.ptr)) 
    { 
    } 

    MyObject& operator=(MyObject&& other) 
    { 
     ptr = std::move(other.ptr); 
     return *this; 
    } 

private: 
    std::unique_ptr<int> ptr; 
}; 

int main(int argc, char* argv[]) 
{ 
    std::vector<MyObject> s; 
    for (int i = 0; i < 5; ++i) 
    { 
     MyObject o; 
     s.push_back(o); 
    } 

    return 0; 
} 
+1

Nếu bạn muốn xây dựng đối tượng của bạn trực tiếp trong vector, bạn cũng có thể chỉ 'emplace' họ: ' cho (int i = 0; i <5; ++ i) s.emplace_back(); 'Điều đó cũng làm việc với VC11. –

Trả lời

11

Hàm push_back() lấy đối số của nó theo giá trị. Do đó, một nỗ lực được thực hiện để sao chép hoặc xây dựng đối số của push_back() (nếu bạn đang chuyển một giá trị), hoặc để di chuyển-xây dựng nó (nếu bạn đang đi qua một rvalue).

Trong trường hợp này, o là một giá trị - bởi vì đối tượng được đặt tên là lvalues ​​ - và tham chiếu rvalue không thể liên kết với giá trị. Do đó, trình biên dịch không thể gọi hàm khởi tạo của bạn.

Để có đối tượng của bạn chuyển, bạn phải viết:

s.push_back(std::move(o)); 
//   ^^^^^^^^^ 

gì làm tôi ngạc nhiên trong trường hợp này là nó có vẻ VC11 tạo ra một bản sao-constructor cho MyObject ngầm mà không cần định nghĩa nó như đã xóa (đánh giá từ lỗi bạn đã đăng). Đây không phải là trường hợp, vì lớp của bạn khai báo một hàm tạo di chuyển. Mỗi đoạn 12,8/7 của tiêu chuẩn C++ 11, trên thực tế:

If the class definition does not explicitly declare a copy constructor, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted (8.4)

Tôi phải kết luận rằng trong khi lỗi bạn đang nhận được là đúng - vì bạn không đi qua một rvalue để push_back() - VC11 không phải là hoàn toàn tuân thủ ở đây.

+0

Tuyệt vời! Tôi đã không nhận thức được điều này. Tôi vẫn còn một chút nhầm lẫn là tại sao các nhà xây dựng di chuyển được gọi là chỉ bằng cách thay đổi unique_ptr thành một chuỗi mặc dù. Nếu VC11 đang tạo ra một hàm tạo bản sao ngầm, tôi sẽ mong đợi rằng sẽ được sử dụng vì tôi không sử dụng std :: move. – zYzil

+1

@zYzil: Hành vi chính xác sẽ không phải là gọi hàm tạo bản sao cũng như hàm tạo di chuyển của 'std :: string' (xem [ví dụ trực tiếp này] (http://liveworkspace.org/code/3kW04t$377)), vì 'MyObject' không thể sao chép được và bạn không di chuyển từ nó. Nếu mã của bạn trông * chính xác * giống như ví dụ của tôi (bao gồm cả thiếu 'std :: move()'), thì VC11 có lỗi. –

+0

Tôi chắc chắn nghĩ rằng có một lỗi trong VC11. [Ví dụ này] (http://liveworkspace.org/code/1edTJY$3) không biên dịch trên GCC 4.8.0; tuy nhiên, trên VC11 nó biên dịch và xuất ra "Move ctor" do hàm tạo đang được gọi. Cảm ơn tất cả sự giúp đỡ của bạn! – zYzil

4

MyObject o; xác định o làm đối tượng. Có nghĩa là nó là một giá trị l. Thực hiện s.push_back(o); sau đó gọi quá tải giá trị l của push_back() (không có lựa chọn nào khác), cố gắng tạo bản sao.

Kể từ khi lớp học của bạn là noncopyable, bạn phải di chuyển đối tượng vào vector:

for (int i = 0; i < 5; ++i) 
{ 
    MyObject o; 
    s.push_back(std::move(o)); 
} 
Các vấn đề liên quan