Сделайте все производные шаблоны класса другом другого класса в C++

Допустим, мне нужна следующая иерархия:

template<class T> class Base {
protected:
    T container;
};

template<class T> class Derived1 : public Base<T> {
public:
    void f1() {
        /* Does stuff with Base<T>::container */
    }
};

template<class T> class Derived2 : public Base<T> {
public:
    void f2() {
        /* Does stuff with Base<T>::container */
    }
};

Теперь я хочу независимый класс (не производный от Base), который может получить доступ к Base<T>::container непосредственно из Base или любого производного класса. Я читал о шаблонных классах друзей, и похоже, что это решение моей проблемы, но я пока не мог понять синтаксис. Я ищу что-то вроде:

template<class T> class Foo{
    template<T> friend class Base<T>; // <-- this does not work
public:
    size_t bar(const Base<T> &b) const{
        return b.container.size();
    }
};

Derived1<std::vector<int> > d;
d.f1();
Foo<std::vector<int> > foo;
size_t s = foo.bar()

Линия класса друга вызывает error: specialization of ‘template<class T> class Base’ must appear at namespace scope template<T> friend class Base<T> и переменная-член container все еще не доступен.

2 ответа

Решение

Пара вопросов:

Так же, как вы не ставите <T> после имени класса при определении шаблона класса:

template <class T> class X<T> { ... }; // WRONG
template <class T> class X { ... };    // RIGHT

Вы не должны помещать его после имени класса при объявлении шаблона класса, будь то в предварительном объявлении или объявлении друга:

template <class T> class X<T>; // WRONG
template <class T> class X;    // RIGHT - declares the template exists
template <class T> friend class X<T>; // WRONG
template <class T> friend class X;    // RIGHT - says all specializations of X are friends

(Если вы не создаете частичную специализацию шаблона класса. Например, если шаблон класса X уже объявлено, тогда template <class T> class X<T*> { ... }; определяет частичную специализацию, которая будет использоваться вместо основного шаблона, когда аргумент шаблона является указателем.)

И у вас есть объявление друга в обратном направлении: оно должно появиться внутри класса с непубличным членом (членами) и назвать другой класс (ы), которым разрешено использовать члены. (Если это работает наоборот, то любой новый класс может получить доступ к закрытым и защищенным членам любого другого класса без разрешения класса-владельца!)

Так что вам нужно:

template <class T> class Foo;

template<class T> class Base {
    template <class U> friend class Foo;
protected:
    T container;
};

Предварительная декларация Foo иногда не требуется, но я думаю, что это делает вещи более понятными, и это может избежать ошибок, когда все становится более сложным с пространствами имен, вложенными классами и т. д.

Только Base можно сказать Foo это его друг.

template<typename T> friend class Foo; // every Foo<T> is a friend of Base
Другие вопросы по тегам