11

Is there a syntax for friending only certain specializations of a templated class, or do you have to friend all specializations with the likes of:

template<class FooType> friend class Bar;

If it existed, I would be looking for something like:

template<class FooType> friend class Bar<FooType const>;

It seems the specializations have their own "friendable" identity, because they are not friends of each other by default. For instance, getFoo() here can't be called by the non-const case derived from the const specialization because it is private:

template<class FooType> class Bar;

template<class FooType>
class Bar<FooType const> {
public:
    Bar (std::string name) : _foo (name) { }
    FooType const * operator->() const
        { return &getFoo(); }
private:
    FooType const & getFoo() const
        { return _foo; }
private:
    FooType _foo;
};

template<class FooType>
class Bar : public Bar<FooType const> {
public:
    Bar (std::string name) : Bar<FooType const> (name) { }
    FooType * operator->()
        { return &getFoo(); }
private:
    FooType & getFoo()
        { return const_cast<FooType &>(Bar<FooType const>::getFoo()); }
};

You have to add template<class FooType> friend Bar; to the const specialization.

(As an aside, the tag description for is sort of funny.)

1 Answers1

12

You are looking for

friend Bar<FooType const>;

which is one of two ways in which friends can be declared. The first syntax declares a class a friend:

friend Type;

where Type can be a simple type Bar (which is not a template), or it can be a specific instantiation of a class template, for example Baz<int>. int can, of course, also be a template parameter from the enclosing class template or whatever you like. The point is: It's a single type that is given friend status.

The second syntax declares a class template to be a friend:

template< ... > friend class ClassTemplate;

where the ... is the template parameter declaration of ClassTemplate. As you can see, you have to specify just the template parameter list, but the list is not used anywhere. It is not possible to combine both syntaxes, there is no way to "partially friend" a class template or to use SFINAE/enable_if to enable or disable certain friend declarations. In fact it doesn't even make much sense to add names to the template parameters, in your above I'd just write

template< class > friend class Bar;

as adding FooType doesn't really add anything of value here.

Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
  • Hm, noticed that Qt Creator gives a red wavy underline and says "expected declaration" if you don't write `class`. Made a [bug report](https://bugreports.qt-project.org/browse/QTCREATORBUG-10486), though their markup did weird things to what I typed and there's no button to edit. The people who write JIRA should try StackOverflow. :-/ – HostileFork says dont trust SE Oct 23 '13 at 16:57
  • This seems to work in my example where you have a class specialization parameter to pass in...or if you have some literal type (e.g. `friend Bar`). But is there a syntax for templating the friend specification itself, without templating the whole class? Let's say I have a *non-templated* `class Baz` that wants to friend all `Bar` for any FooType. As mentioned, `template friend Bar;` gets an invalid template error... – HostileFork says dont trust SE Oct 23 '13 at 17:19
  • @HostileFork So you want to make all `Bar` friends as long as `T` satisfies a condition? Interesting question. I'm not sure there is a solution, but I'll play with some code now... – Daniel Frey Oct 23 '13 at 17:33
  • @HostileFork I think it is not possible for the following reason: Each type has a list of a) friend classes and b) a list of friend class templates, each defined with their own syntax. If you'd want something like: All `Foo` are friends, it would mean that you'd have to "instantiate a friend declaration" to see if it matches on every attempted access from some other type or if it is disabled by SFINAE or something similar. The current C++11 does not have anything even remotely close to that, therefore I think it's impossible to do. An interesting idea, though. – Daniel Frey Oct 23 '13 at 17:48