Making a template parameter a friend?
Solution 1
It is explicitly disallowed in the standard, even if some versions of VisualStudio do allow it.
C++ Standard 7.1.5.3 Elaborated type specifiers, paragraph 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. ]
I recognize the code above as a pattern to seal (disallow the extension of) a class. There is another solution, that does not really block the extension but that will flag unadvertidly extending from the class. As seen in ADOBE Source Library:
namespace adobe { namespace implementation {
template <class T>
class final
{
protected:
final() {}
};
}}
#define ADOBE_FINAL( X ) private virtual adobe::implementation::final<T>
with the usage:
class Sealed : ADOBE_FINAL( Sealed )
{//...
};
While it allows extension if you really force it:
class SealBreaker : public Sealed, ADOBE_FINAL( Sealed )
{
public:
SealBreaker() : adobe::implementation::final<Sealed>(), Sealed() {}
};
It will restrict users from mistakenly do it.
EDIT:
The upcoming C++11 standard does allow you to befriend a type argument with a slightly different syntax:
template <typename T>
class A {
// friend class T; // still incorrect: elaborate type specifier
friend T; // correct: simple specifier, note lack of "class"
};
Solution 2
I found a simple trick to declare template parameters as friends:
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
However I do not know how this could help to define an alternative version of a class sealer.
Solution 3
Do you really need to do this? If you want to prevent someone from deriving from your class, just add a comment and make the destructor non-virtual.
Related videos on Youtube
Jonas
I'm just a regular dude who likes games and to eat strawberries.
Updated on March 26, 2020Comments
-
Jonas over 4 years
Example:
template<class T> class Base { public: Base(); friend class T; };
Now this doesn't work... Is there a way of doing this?
I'm actually trying to make a general class sealer like this:
class ClassSealer { private: friend class Sealed; ClassSealer() {} }; class Sealed : private virtual ClassSealer { // ... }; class FailsToDerive : public Sealed { // Cannot be instantiated };
I found this example on this site somewhere but I can't find it... (here)
I know there are other ways of doing this but just now I'm curious if you actually can do something like this.
-
Ankit Roy about 15 yearsSure, but it's better if illegal usage can be flagged at compile time, is it not? It's the same principle as using an assert() instead of a comment -- wouldn't you agree that assert() is useful?
-
Armen Tsirunyan over 13 yearsSometimes you just can't make the destructor non-virtual, because it might have a base class where the destructor is virtual.
-
zennehoy about 12 yearsAny idea how standard-conforming this is? Works great though, thanks for the tip!
-
onitake almost 12 yearsLooks pretty standard-conforming to me, but I'm no standard guru. Nice find!
-
Viren about 11 yearsNot quite...clang gives me below error: error: elaborated type refers to a typedef friend class TypeWrapper<T>::type;
-
blue scorpion about 7 years...then again, C++11 allows for the keyword "final", for example: class X final{...} (or you can make individual virtual functions final). In any case, I've tried the code above ("friend T;") with g++ 4.8.4 without the -std=c++11 flag and it compiles fine.