2013-06-19 23 views
7

Giả sử tôi có một lớp cơ sở mà nhân bản của các lớp thừa:nguồn gốc mẫu tò mò lặp đi lặp lại và hiệp phương sai

class Base 
{ 
    public: 
     virtual Base * clone() 
     { 
      return new Base(); 
     } 

     // ... 
}; 

tôi có một tập hợp các lớp thừa kế được thực hiện bằng cách sử dụng mô hình mẫu một cách tò mò lặp đi lặp lại:

template <class T> 
class CRTP : public Base 
{ 
    public: 
     virtual T * clone() 
     { 
      return new T(); 
     } 

     // ... 
}; 

Và tôi cố gắng lấy thêm từ đó như sau:

class Derived : public CRTP<Derived> 
{ 
    public: 
     // ... 
}; 

Tôi nhận được lỗi biên dịch về ảnh hưởng của:

error C2555: 'CRTP<T>::clone': overriding virtual function return type differs and is not covariant from 'Base::clone' 

Tôi nhận thấy đây có thể là kết quả của trình biên dịch không biết đầy đủ về cây thừa kế cho nguồn gốc khi khởi tạo CRTP. Hơn nữa, thay thế kiểu trả về (T *) bằng (Base *) cũng biên dịch. Tuy nhiên, tôi muốn biết nếu có một công việc xung quanh mà vẫn giữ lại ngữ nghĩa trên.

+1

Đối với những gì đáng giá, GCC 4.1.2 và 4.7.1 đều cung cấp các lỗi tương tự. –

+1

Bản sao của http://stackoverflow.com/questions/15570333/crtp-and-dynamic-polymorphism-compile-error – erikced

+0

@erikced Cảm ơn những người đứng đầu. Có vẻ như tôi không thể làm gì nhiều, tôi sẽ chỉ thay thế kiểu trả về. – Whanhee

Trả lời

3

Cách giải quyết không tốt lắm.

class Base 
{ 
    protected: 
     virtual Base * clone_p() 
     { 
      return new Base(); 
     } 
}; 


template <class T> 
class CRTP : public Base 
{ 
    protected: 
     virtual CRTP* clone_p() 
     { 
      return new T; 
     } 
    public: 
     T* clone() 
     { 
      CRTP* res = clone_p(); 
      return static_cast<T*>(res); 
     } 
}; 


class Derived : public CRTP<Derived> 
{ 
    public: 
}; 

Sử dụng dynamic_cast<> thay vì static nếu bạn cảm thấy an toàn hơn.

+0

Cảm ơn sự giúp đỡ. Không hoàn toàn ngữ nghĩa tôi đang tìm kiếm, vì lý tưởng cơ sở sẽ có cùng một clone() gọi. – Whanhee

1

Nếu bạn có thể sống với việc phải sử dụng một cú pháp khác nhau để xác định loại hoàn tất, bạn có thể làm như sau (cảnh báo: Mã chưa được kiểm tra): khởi đầu

Hãy với máy móc thiết bị:

// this gives the complete type which needs to be used to create objects 
// and provides the implementation of clone() 
template<typename T> class Cloneable: 
    public T 
{ 
public: 
    template<typename... U> Cloneable(U&&... u): T(std::forward<U>(u) ...) {} 
    T* clone() { return new Cloneable(*this); } 
private: 
    // this makes the class complete 
    // Note: T:: to make it type dependent, so it can be found despite not yet defined 
    typename T::CloneableBase::CloneableKey unlock() {} 
}; 

// this provides the clone function prototype and also makes sure that only 
// Cloneable<T> can be instantiated 
class CloneableBase 
{ 
    template<typename T> friend class Cloneable; 

    // this type is only accessible to Clonerable instances 
    struct CloneableKey {}; 

    // this has to be implemented to complete the class; only Cloneable instances can do that 
    virtual CloneableKey unlock() = 0; 
public: 
    virtual CloneableBase* clone() = 0; 
    virtual ~CloneableBase() {} 
}; 

OK, bây giờ là phân cấp lớp thực tế. Đó là một tiêu chuẩn khá; không có trung gian CRTP hoặc các biến chứng khác. Tuy nhiên, không có lớp nào thực hiện hàm clone, nhưng tất cả đều thừa kế khai báo (trực tiếp hoặc gián tiếp) từ CloneableBase.

// Base inherits clone() from CloneableBase 
class Base: 
    public CloneableBase 
{ 
    // ... 
}; 

// Derived can inherit normally from Base, nothing special here 
class Derived: 
    public Base 
{ 
    // ... 
}; 

Đây là cách bạn sau đó tạo ra các đối tượng:

// However, to create new instances, we actually need to use Cloneable<Derived> 
Cloneable<Derived> someObject; 
Derived* ptr = new Cloneable<Derived>(whatever); 

// Now we clone the objects 
Derived* clone1 = someObject.clone(); 
Derived* clone2 = ptr->clone(); 

// we can get rid og the objects the usual way: 
delete ptr; 
delete clone1; 
delete clone2; 

Lưu ý rằng một Cloneable<Derived> là-một Derived (nó là một lớp con), do đó bạn cần phải sử dụng Cloneable chỉ cho xây dựng, và nếu không thể giả vờ để làm việc với các đối tượng Derived (tốt, tyepinfo cũng sẽ xác định đối tượng là Cloneable<Derived>).