2010-12-11 18 views
15

Sử dụng CRTP đôi khi tôi viết một mã như thế này:Làm thế nào để tránh lỗi trong khi sử dụng CRTP?

// this was written first 
struct Foo : Base<Foo, ...> 
{ 
    ... 
}; 

// this was copy-pasted from Foo some days later 
struct Bar : Base<Foo, ...> 
{ 
    ... 
}; 

Và nó rất khó khăn để hiểu những gì sai, cho đến khi tôi theo dõi mã trong trình gỡ lỗi và thấy rằng các thành viên Bar không được sử dụng trong Base.

Làm cách nào để hiển thị lỗi này khi biên dịch?

(tôi sử dụng MSVC2010, vì vậy tôi có thể sử dụng một số C++ 0x tính năng và mở rộng ngôn ngữ MSVC)

Trả lời

12

Trong C++ 0x bạn có một giải pháp đơn giản. Tôi không biết liệu nó được thực hiện trong MSVC10 tuy nhiên.

template <typename T> 
struct base 
{ 
private: 
    ~base() {} 
    friend T; 
}; 

// Doesn't compile (base class destructor is private) 
struct foo : base<bar> { ... }; 
+0

thực sự nó không hoạt động nếu dtor không bao giờ được gọi. – Abyx

+0

@Abyx: Thật thú vị với gcc 4.9, nếu tôi sử dụng vị trí mới để xây dựng một đối tượng kiểu 'struct S: base {}', nó phàn nàn về hàm tạo * 'S :: S()' bị xóa hoàn toàn do '~ base' là riêng tư. Tuy nhiên trong trường hợp này destructor không bao giờ được gọi. –

+0

Thú vị hơn, gcc 4.8.1 không phàn nàn gì cả! –

0

Không có cách nào để biết loại bắt nguồn. Bạn có thể thực thi rằng Foo bắt nguồn từ Base<Foo>, nhưng bạn không thể thực thi rằng không có lớp nào khác cũng lấy được từ đó.

0

tôi có thể sử dụng một macro

#define SOMENAMESPACE_BASE(type, arg1, arg2) type : Base<type, arg1, arg2> 

nhưng tôi không muốn sử dụng các macro nếu giải pháp tốt hơn tồn tại.

10

Bạn có thể sử dụng một cái gì đó như thế này:

template<class T> class Base { 
protected: 
    // derived classes must call this constructor 
    Base(T *self) { } 
}; 

class Foo : public Base<Foo> { 
public: 
    // OK: Foo derives from Base<Foo> 
    Foo() : Base<Foo>(this) { } 
}; 

class Moo : public Base<Foo> { 
public: 
    // error: constructor doesn't accept Moo* 
    Moo() : Base<Foo>(this) { } 
}; 

class Bar : public Base<Foo> { 
public: 
    // error: type 'Base<Bar>' is not a direct base of 'Bar' 
    Bar() : Base<Bar>(this) { } 
}; 
+0

Nó thực sự tiết lộ khi Foo là một mẫu. –

+3

@Alexandre: các mẫu có độ dài. – Amnon

+0

có, nhưng trong mã sản xuất mã của bạn sẽ khó sử dụng hơn CRTP đồng bằng (tôi đã cố gắng một lần để sử dụng một cái gì đó dọc theo những dòng này vì lý do tương tự như OP). –

2
template<typename T, int arg1, int arg2> 
struct Base 
{ 
    typedef T derived_t; 
}; 

struct Foo : Base<Foo, 1, 2> 
{ 
    void check_base() { Base::derived_t(*this); } // OK 
}; 

struct Bar : Base<Foo, 1, 2> 
{ 
    void check_base() { Base::derived_t(*this); } // error 
}; 

Mã này được dựa trên Amnon's answer, nhưng kiểm tra mã không chứa tên của lớp được thừa kế, vì vậy tôi có thể sao chép và dán nó mà không thay đổi.

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