2012-02-23 28 views
6

Tôi đang gặp rắc rối hiểu tại sao các nhà xây dựng Foo di chuyển cố gắng gọi ~ptr trong ví dụ sau:Trường hợp nào destructor ẩn trong mã này?

#include <utility> 

template <typename T, typename Policy> 
class ptr { 
    T * m_t; 
public: 
    ptr() noexcept : m_t(0) {} 
    explicit ptr(T *t) noexcept : m_t(t) {} 
    ptr(const ptr &other) noexcept : m_t(Policy::clone(other.m_t)) {} 
    ptr(ptr &&other) noexcept : m_t(other.m_t) { other.m_t = 0; } 
    ~ptr() noexcept { Policy::delete_(m_t); } 
    ptr &operator=(const ptr &other) noexcept 
    { ptr copy(other); swap(copy); return *this; } 
    ptr &operator=(ptr &&other) noexcept 
    { std::swap(m_t,other.m_t); return *this; } 

    void swap(ptr &other) noexcept { std::swap(m_t, other.m_t); } 

    const T * get() const noexcept { return m_t; } 
    T * get() noexcept { return m_t; } 
}; 

class FooPolicy; 
class FooPrivate; 
class Foo { 
    // some form of pImpl: 
    typedef ptr<FooPrivate,FooPolicy> DataPtr; 
    DataPtr d; 
public: 
    // copy semantics: out-of-line 
    Foo(); 
    Foo(const Foo &other); 
    Foo &operator=(const Foo &other); 
    ~Foo(); 

    // move semantics: inlined 
    Foo(Foo &&other) noexcept 
     : d(std::move(other.d)) {} // l.35 ERR: using FooDeleter in ~ptr required from here 
    Foo &operator=(Foo &&other) noexcept 
    { d.swap(other.d); return *this; } 
}; 

GCC 4.7:

foo.h: In instantiation of ‘ptr<T, Policy>::~ptr() [with T = FooPrivate; Policy = FooPolicy]’: 
foo.h:34:44: required from here 
foo.h:11:14: error: incomplete type ‘FooPolicy’ used in nested name specifier 

Clang 3.1-pre:

foo.h:11:14: error: incomplete type 'FooPolicy' named in nested name specifier 
    ~ptr() { Policy::delete_(m_t); } 
      ^~~~~~~~ 
foo.h:34:5: note: in instantiation of member function 'ptr<FooPrivate, FooPolicy>::~ptr' requested here 
    Foo(Foo &&other) : d(std::move(other.d)) {} 
    ^
foo.h:23:7: note: forward declaration of 'FooPolicy' 
class FooPolicy; 
    ^
foo.h:11:20: error: incomplete definition of type 'FooPolicy' 
    ~ptr() { Policy::delete_(m_t); } 
      ~~~~~~^~ 
2 errors generated. 

Điều gì đang xảy ra? Tôi đang viết các nhà thầu di chuyển để tránh chạy các bản sao ctors và dtors. Lưu ý rằng đây là tệp tiêu đề cố gắng ẩn triển khai của nó (thành ngữ pimpl), do đó, việc tạo FooDeleter một loại đầy đủ không phải là một tùy chọn.

EDIT: Sau câu trả lời của Bo, tôi đã thêm noexcept ở mọi nơi tôi có thể (chỉnh sửa ở trên). Nhưng các lỗi vẫn như cũ.

+2

nhìn chằm chằm vào mã đó trong một giây và nhận được một cơn sốt nhỏ ngay lập tức, cảm ơn câu hỏi, tôi có một vấn đề tương tự gần đây may mắn không theo đuổi – lurscher

+1

Không phải là câu trả lời cho câu hỏi của bạn. Deleter (m_t); 'không có nghĩa là bạn nghĩ nó có nghĩa là gì. Nó định nghĩa một biến được sử dụng không được sử dụng dư thừa 'm_t' của kiểu' Deleter'. – hvd

+1

"Tôi đang viết các nhà thầu di chuyển để tránh chạy các bản sao ctors và dtors." Đó không phải là một lý do chính đáng để viết một hàm tạo di chuyển. Lý do để viết các nhà xây dựng di chuyển là vì nó là một loại container cấp thấp cần di chuyển ngữ nghĩa, hoặc bạn đang sử dụng Visual Studio mà không ngầm tạo ra các hàm tạo di chuyển cho bạn. Nếu không, hãy để trình biên dịch thực hiện công việc của nó. –

Trả lời

9

Bạn tạo đối tượng Foo mới có chứa thành viên ptr<something>. Trong trường hợp nhà xây dựng Foo không thành công, trình biên dịch phải gọi hàm hủy cho bất kỳ thành viên được xây dựng hoàn toàn nào được xây dựng một phần Foo.

Nhưng nó không thể khởi tạo ~ptr<incomplete_type>(), do đó không thành công.

Bạn có trường hợp tương tự với trình phá hủy riêng tư. Điều đó cũng ngăn bạn tạo các đối tượng thuộc loại đó (trừ khi được thực hiện từ một người bạn hoặc một hàm thành viên).

+2

+1 và giải pháp trong trường hợp này có thể đơn giản như di chuyển tất cả các định nghĩa hàm tạo và hủy cho tệp triển khai nơi tất cả các loại được hoàn tất. –

+0

+1; Nói cách khác, bạn không thể định nghĩa bất kỳ phương thức 'Foo' nào trong dòng vì bạn đang ẩn ** tất cả ** chi tiết ở cấp tiêu đề. Giả sử những thay đổi từ [comment trước] của tôi (http: // stackoverflow.com/questions/9417477/where-does-the-destructor-hide-in-this-code # comment12270235_9417477), bạn có thể sử dụng 'Foo(): d (new FooPrivate) {} Foo (Foo const &) = mặc định; Foo (Foo &&) = mặc định; ~ Foo() = mặc định; Foo & operator = (Foo) = mặc định; 'như các triển khai, vì' ptr' xử lý đúng mọi thứ. – CTMacUser

+0

@Bo: Câu trả lời của bạn có vẻ hợp lý cho đến khi tôi thử dán tất cả 'ptr', cũng như toán tử di chuyển' Foo' và toán tử gán với 'noexcept'. AFAIU, đó là lý do ban đầu cho 'noexcept': cho phép di chuyển ctors để ném. Tôi biết tôi không thể, vậy tại sao GCC vẫn gọi dtor? Tôi đã nói với nó không có gì có thể thất bại, phải không? –

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