2013-04-24 59 views
6

Mã biên dịch hoàn toàn tốt (trên GCC 4.7.2) khi tôi không có một destructor do người dùng xác định, tạo ra lỗi ngay cả khi rỗng được cung cấp:Cung cấp một destructor do người dùng định nghĩa (trống) gây ra lỗi biên dịch

#include <memory> 

class Test 
{ 
    std::unique_ptr<int> val; 
}; 

template <typename Type> 
class B 
{ 
public: 
    //destructor: 
    // if I comment this out, the code compiles just fine: 
    ~B() { } 

private: 
    Test a; 
}; 

int main() 
{ 
    auto s = B<int>(); 
} 

những điểm nổi bật trong các lỗi sản xuất khi destructor không nhận xét ra là:

  • các nhà xây dựng bản sao cho thử nghiệm không tồn tại, và không được mặc nhiên tạo ra vì nó sẽ là bệnh hình thành
  • Đã xảy ra sự cố khi sử dụng hàm tạo bản sao đã xóa cho unique_ptr.

Đầu ra lỗi hoàn chỉnh, cho bất kỳ ai quan tâm, nằm ở cuối bài đăng này.

Tôi biết rằng unique_ptr không thể sao chép được xây dựng (trừ khi đối số là một giá trị), và do đó trình biên dịch sao chép ngầm không hợp lệ cho lớp Test.

Điều tôi không thể hiểu được là lý do tại sao xác định ngay cả một số trống hủy đột nhiên sẽ yêu cầu các cơ sở sao chép này. Rõ ràng, khi những thứ như unique_ptr s được sử dụng, điều này không thể cung cấp.

Có ai ở đây có thể khai sáng cho tôi xem tại sao lại xảy ra trường hợp này?

ra lỗi Complete khi destructor không nhận xét ra:

In file included from /usr/include/c++/4.7/list:64:0, 
       from ../../Dropbox/Programming/C++/test/main.cpp:2: 
/usr/include/c++/4.7/bits/stl_list.h: In instantiation of 'std::_List_node<_Tp>::_List_node(_Args&& ...) [with _Args = {const Test&}; _Tp = Test]': 
/usr/include/c++/4.7/ext/new_allocator.h:110:4: required from 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::_List_node<Test>; _Args = {const Test&}; _Tp = std::_List_node<Test>]' 
/usr/include/c++/4.7/bits/stl_list.h:503:8: required from 'std::list<_Tp, _Alloc>::_Node* std::list<_Tp, _Alloc>::_M_create_node(_Args&& ...) [with _Args = {const Test&}; _Tp = Test; _Alloc = std::allocator<Test>; std::list<_Tp, _Alloc>::_Node = std::_List_node<Test>]' 
/usr/include/c++/4.7/bits/stl_list.h:1533:63: required from 'void std::list<_Tp, _Alloc>::_M_insert(std::list<_Tp, _Alloc>::iterator, _Args&& ...) [with _Args = {const Test&}; _Tp = Test; _Alloc = std::allocator<Test>; std::list<_Tp, _Alloc>::iterator = std::_List_iterator<Test>]' 
/usr/include/c++/4.7/bits/stl_list.h:997:9: required from 'void std::list<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = Test; _Alloc = std::allocator<Test>; std::list<_Tp, _Alloc>::value_type = Test]' 
/usr/include/c++/4.7/bits/stl_list.h:1466:6: required from 'void std::list<_Tp, _Alloc>::_M_initialize_dispatch(_InputIterator, _InputIterator, std::__false_type) [with _InputIterator = std::_List_const_iterator<Test>; _Tp = Test; _Alloc = std::allocator<Test>]' 
/usr/include/c++/4.7/bits/stl_list.h:582:9: required from 'std::list<_Tp, _Alloc>::list(const std::list<_Tp, _Alloc>&) [with _Tp = Test; _Alloc = std::allocator<Test>; std::list<_Tp, _Alloc> = std::list<Test>]' 
../../Dropbox/Programming/C++/test/main.cpp:11:7: required from here 
/usr/include/c++/4.7/bits/stl_list.h:115:71: error: use of deleted function 'Test::Test(const Test&)' 
../../Dropbox/Programming/C++/test/main.cpp:5:7: note: 'Test::Test(const Test&)' is implicitly deleted because the default definition would be ill-formed: 
../../Dropbox/Programming/C++/test/main.cpp:5:7: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>; std::unique_ptr<_Tp, _Dp> = std::unique_ptr<int>]' 
In file included from /usr/include/c++/4.7/memory:86:0, 
       from ../../Dropbox/Programming/C++/test/main.cpp:3: 
/usr/include/c++/4.7/bits/unique_ptr.h:262:7: error: declared here 
+0

Aah đó là điều cần biết; Tôi đã được ấn tượng rằng cho dù các nhà xây dựng di chuyển tiềm ẩn/nhà khai thác chuyển nhượng đã được tạo ra hay không vẫn còn hit và bỏ lỡ tùy thuộc vào trình biên dịch hoặc một cái gì đó. Tôi không nhận ra nó là một phần của tiêu chuẩn. – jsdw

Trả lời

13

Nếu bạn xác định ~B(), điều này ngăn chặn các nhà xây dựng di chuyển của B, do đó trình biên dịch sẽ cố gắng để tạo ra một constructor sao chép nhưng không thành công vì unique_ptr không thể sao chép được.

Nếu bạn bỏ qua ~B(), hàm tạo di chuyển của B được tạo và được sử dụng trong main.

Bạn có thể yêu cầu các nhà xây dựng di chuyển tự động tạo ra:

B(B &&) = default; 

Đây là một tính năng trong các tiêu chuẩn để tương thích ngược với C++ 03 mã; theo Quy tắc Ba, mã viết hàm hủy riêng của nó (v.v.) được giả định là quản lý tài nguyên riêng của nó để tự động tạo ra một hàm tạo di chuyển sẽ không thích hợp trừ khi được yêu cầu một cách rõ ràng.

+0

Tôi có suy nghĩ đúng không, rằng dòng 'auto s = B ();' đầu tiên xây dựng một B tạm thời, sau đó gọi toán tử chuyển động ngầm của B (trong s), mà lần lượt gọi hàm khởi động ngầm của B? – jsdw

+0

@lytnus 'auto s = B ()' là một * khởi tạo *, không phải là một nhiệm vụ, do đó, gọi một hàm tạo (mặc dù hàm tạo có thể được elided, nó phải có sẵn). – ecatmur

+0

aah ok có ý nghĩa với tôi! Cảm ơn :) – jsdw

3

Vâng, bạn đã tự mình nói; khi bạn cung cấp một destructor do người dùng định nghĩa, bạn ức chế khả năng của trình biên dịch để tạo ra những thứ như một hàm tạo di chuyển ngầm. Nội dung của người hủy do người dùng xác định (có thể là trống hoặc cách khác) hoàn toàn không có liên quan.

[C++11: 12.7/9]:Nếu định nghĩa của một lớp X không tuyên bố một cách rõ ràng một constructor di chuyển, người ta sẽ được khai báo ngầm như mặc định khi và chỉ khi:

  • X không có một người dùng tuyên bố copy constructor,
  • X không có toán tử gán bản sao do người dùng khai báo,
  • X không có một toán tử gán di chuyển do người dùng khai báo,
  • X không có một destructor sử dụng tuyên bố
  • các nhà xây dựng di chuyển sẽ không được định nghĩa ngầm như bị xóa.

[Ghi chú:Khi các nhà xây dựng di chuyển không được mặc nhiên tuyên bố hoặc cung cấp một cách rõ ràng, biểu thức mà nếu không sẽ gọi các nhà xây dựng di chuyển thay vì có thể gọi một constructor sao chép.-end note]

Văn bản trong những điểm lưu ý các trường hợp chính xác mà bạn đang nhìn thấy, dẫn đến một thất bại biên soạn vì unique_ptrkhông thể được sao chép; mặc dù bạn đang không sao chép và mặc dù việc khởi tạo s có thể không yêu cầu sao chép/di chuyển, nhưng vẫn cần có thao tác này (mỗi [C++11: 12.8/31-32]).

+0

Xin cảm ơn, thật tốt khi biết rằng hàm tạo di chuyển được định nghĩa ngầm định như một phần của tiêu chuẩn; Tôi đã ấn tượng rằng nó đã bị đánh và bỏ lỡ liệu trình biên dịch có chọn làm điều đó cho bạn hay không (mặc dù tôi chắc chắn rằng tôi vẫn sẽ bị nhầm lẫn bất kể!) – jsdw

+1

@lytnus bạn có thể đã nhận được ấn tượng đó vì thiếu MSVC thế hệ ngầm đó. –

+1

Có; thẳng thắn, nó bị đánh và bỏ lỡ là liệu MSVC có chọn không làm _anything_ cho bạn hay không, những ngày này. –