2013-03-22 22 views
6
class A { 
    virtual A* foo() = 0; 
}; 

template<class T> 
class B : public A { 
    virtual T* foo() { return nullptr; } 
}; 

class C : public B<C> { 

}; 

Đây là cách triển khai đơn giản cho Possibility to mix composite pattern and curiously recurring template pattern. Tôi nhận được lỗi sau:Lỗi biên dịch CRTP và đa hình động

Return type of virtual function 'foo' is not covariant with the return type of the function it overrides ('C *' is not derived from 'A *') 

Thử nghiệm trên kêu vang 3.0, gcc 4.7 và visual studio 2008.

giải pháp đầu tiên:

class C : public A, public B<C> {} 

biên dịch dưới visual studio với một cảnh báo rằng B đã con của A và không không phải biên dịch dưới tiếng kêu có lỗi ban đầu.

Một workaround:

class D : public A {} 
class C : public B<D> {} 

giải quyết vấn đề bất toàn, nhưng tôi không thể tìm ra bao nhiêu trường hợp A Tôi sẽ có. Trực giác nói với tôi rằng A là ảo, do đó sẽ chỉ có một.

Giải pháp thay thế này cũng tạo mã không đọc được.

Trạng thái chuẩn nói về tình huống này là gì? Mã này có nên biên dịch không? Nếu không, tại sao?

+2

Luôn ghi nhớ rằng 'C' không đầy đủ trong danh sách các căn cứ. –

Trả lời

4

Chức năng ảo của bạn A::foo() trả lại số A*, trong khi chức năng B<C>::foo(), có nghĩa là ghi đè lên, trả về C*. Điều này trong lý thuyết không tôn trọng các nguyên tắc hiệp phương sai, kể từ C thực sự là một chuyên ngành (có nguồn gốc từ) A, nhưng tại thời điểm instantiation, điều này không được biết, bởi vì C là một loại không đầy đủ.

Một cách tốt để suy nghĩ lại về thiết kế của bạn là làm cho A một lớp mẫu là tốt, và để B tuyên truyền các mẫu đối số cho T lên đến A:

template<typename T> 
class A { 
    virtual T* foo() = 0; 
}; 

template<class T> 
class B : public A<T> { 
    virtual T* foo() { return nullptr; } 
}; 

Về workaround của bạn:

What does the standard states about this situation? Should this code compile? If not, why?

Nó không nên biên dịch, bởi vì thực tế chỉ làm C cũng xuất phát từ A một cách rõ ràng (thông báo, rằng bạn sẽ kết thúc với hai các đối tượng con cơ sở riêng biệt loại A bên trong C) không làm cho C một loại hoàn chỉnh khi khởi tạo B<C>. Theo Đoạn 9.2/2 của tiêu chuẩn C++ 11:

A class is considered a completely-defined object type (3.9) (or complete type) at the closing } of the class-specifier. Within the class member-specification, the class is regarded as complete within function bodies, default arguments, and brace-or-equal-initializers for non-static data members (including such things in nested classes). Otherwise it is regarded as incomplete within its own class member-specification.

+0

Thật không may điều này phá vỡ mục đích duy nhất của mã này: để tạo lớp cơ sở đa hình A. –

+0

* chức năng B :: foo(), có nghĩa là ghi đè nó, trả về B ** - tôi đọc sai hay sao 'B :: foo()' return 'C *' thay thế? – ulidtko

+1

@ulidtko: Đúng vậy, đó là lỗi của tôi. Cảm ơn bạn. –