2009-03-31 2 views
30

Ví dụ:Tạo thông số mẫu cho một người bạn?

template<class T> 
class Base { 
public: 
    Base(); 
    friend class T; 
}; 

Bây giờ điều này không có tác dụng ... Có cách nào để thực hiện việc này không?

Tôi thực sự cố gắng để làm một niêm phong lớp chung như thế này:

class ClassSealer { 
private: 
    friend class Sealed; 
    ClassSealer() {} 
}; 
class Sealed : private virtual ClassSealer 
{ 
    // ... 
}; 
class FailsToDerive : public Sealed 
{ 
    // Cannot be instantiated 
}; 

tôi thấy ví dụ này trên trang web này ở đâu đó nhưng tôi không thể tìm thấy nó ... (here)

Tôi biết có other ways làm điều này nhưng bây giờ tôi rất tò mò nếu bạn thực sự có thể làm một cái gì đó như thế này.

Trả lời

32

Điều này rõ ràng không được phép trong tiêu chuẩn, ngay cả khi một số phiên bản của VisualStudio cho phép.

C++ chuẩn 7.1.5.3 xây dựng loại specifiers, đoạn 2

3.4.4 describes how name lookup proceeds for the identifier in an elaborated-type-specifier. If the identifier resolves to a class-name or enum-name, the elaborated-type-specifier introduces it into the declaration the same way a simple-type-specifier introduces its type-name. If the identifier resolves to a typedef-name or a template type-parameter, the elaborated-type-specifier is ill-formed. [Note: this implies that, within a class template with a template type-parameter T, the declaration friend class T; is ill-formed. ]

tôi nhận ra đoạn mã trên như một mô hình để đóng dấu (không cho phép gia hạn) một lớp. Có một giải pháp khác, điều đó không thực sự chặn phần mở rộng nhưng nó sẽ tự động mở rộng ra khỏi lớp. Như đã thấy trong ADOBE Source Library:

namespace adobe { namespace implementation { 
template <class T> 
class final 
{ 
protected: 
    final() {} 
}; 
}} 
#define ADOBE_FINAL(X) private virtual adobe::implementation::final<T> 

với việc sử dụng:

class Sealed : ADOBE_FINAL(Sealed) 
{//... 
}; 

Trong khi nó cho phép mở rộng nếu bạn thực sự buộc nó:

class SealBreaker : public Sealed, ADOBE_FINAL(Sealed) 
{ 
public: 
    SealBreaker() : adobe::implementation::final<Sealed>(), Sealed() {} 
}; 

Nó sẽ hạn chế người dùng từ sai lầm làm điều đó.

EDIT:

Tiêu chuẩn C++ 11 sắp tới không cho phép bạn làm bạn với một đối số kiểu với một cú pháp hơi khác nhau:

template <typename T> 
class A { 
    // friend class T; // still incorrect: elaborate type specifier 
    friend T;   // correct: simple specifier, note lack of "class" 
}; 
+0

... sau đó một lần nữa, C++ 11 cho phép từ khóa "cuối cùng", ví dụ: lớp X final {...} (hoặc bạn có thể tạo các hàm ảo riêng lẻ cuối cùng). Trong mọi trường hợp, tôi đã thử đoạn mã trên ("friend T;") với g ++ 4.8.4 _without_ the -std = C++ 11 flag và nó biên dịch tốt. –

3

Bạn có thực sự cần thực hiện việc này không? Nếu bạn muốn ngăn không cho ai đó xuất phát từ lớp học của bạn, chỉ cần thêm nhận xét và làm cho trình hủy không phải là ảo.

+0

:) Đôi khi, câu trả lời kỹ thuật tốt nhất không phải là kỹ thuật. –

+1

Chắc chắn, nhưng tốt hơn nếu sử dụng trái phép có thể được gắn cờ tại thời gian biên dịch, phải không? Đó là nguyên tắc tương tự như sử dụng một khẳng định() thay vì một bình luận - bạn sẽ không đồng ý rằng khẳng định() là hữu ích? –

+1

Đôi khi bạn chỉ không thể làm cho destructor không ảo, bởi vì nó có thể có một lớp cơ sở nơi destructor là ảo. –

16

tôi tìm thấy một thủ thuật đơn giản để khai báo các thông số mẫu như bạn bè:

template < typename T> 
struct type_wrapper 
{ 
    typedef T type; 
}; 


template < typename T> class foo 
{ 
    friend class type_wrapper < T>::type 
}; // type_wrapper< T>::type == T 

Tuy nhiên tôi không biết điều này có thể giúp xác định phiên bản thay thế của lớp lót .

+0

Bất kỳ ý tưởng làm thế nào tiêu chuẩn phù hợp này là?Hoạt động tốt mặc dù, cảm ơn cho tip! – zennehoy

+0

Trông khá chuẩn phù hợp với tôi, nhưng tôi không có guru tiêu chuẩn. Nice tìm! – onitake

+1

Không hoàn toàn ... tiếng kêu cho tôi dưới đây lỗi: lỗi: kiểu xây dựng đề cập đến một typedef lớp người bạn TypeWrapper :: type; – Viren