49

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.

Community
  • 1
  • 1
Jonas
  • 1,532
  • 3
  • 16
  • 20

3 Answers3

50

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"
};
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • ...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. – blue scorpion May 26 '17 at 19:02
  • As of 2023 your addendum "The upcoming C++11 standard does allow..." should come first in your answer IMHO. It's easily overlooked otherwise. – Elmar Zander Feb 21 '23 at 21:57
22

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.

sarnold
  • 102,305
  • 22
  • 181
  • 238
A Milton
  • 221
  • 2
  • 3
1

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.

rlbond
  • 65,341
  • 56
  • 178
  • 228