Making a template parameter a friend?

11,564

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.

Share:
11,564

Related videos on Youtube

Jonas
Author by

Jonas

I'm just a regular dude who likes games and to eat strawberries.

Updated on March 26, 2020

Comments

  • Jonas
    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
    Ankit Roy about 15 years
    Sure, 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
    Armen Tsirunyan over 13 years
    Sometimes you just can't make the destructor non-virtual, because it might have a base class where the destructor is virtual.
  • zennehoy
    zennehoy about 12 years
    Any idea how standard-conforming this is? Works great though, thanks for the tip!
  • onitake
    onitake almost 12 years
    Looks pretty standard-conforming to me, but I'm no standard guru. Nice find!
  • Viren
    Viren about 11 years
    Not quite...clang gives me below error: error: elaborated type refers to a typedef friend class TypeWrapper<T>::type;
  • blue scorpion
    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.