объявить шаблон класса другом

По некоторым причинам я хотел бы написать такой код:

template<class T>
class C : public T
{
friend class T;
};

Думаю код понятен. Я хочу иметь шаблон класса, который определяет класс, производный от класса, переданного ему в качестве параметра шаблона, и, чтобы немного усложнить ситуацию, я хотел бы определить базовый класс как друг производного. Код выглядит нормально с компилятором MSVC, но компилятор GNU C++ много жалуется. Что нужно сделать, чтобы получить желаемый функционал?


person modjtabaf    schedule 26.10.2011    source источник
comment
зачем друг, если ты из него вытекаешь?   -  person Nim    schedule 26.10.2011
comment
Что конкретно говорит GCC?   -  person Oliver Charlesworth    schedule 26.10.2011
comment
@Nim: например, вы можете использовать CRTP для реализации имитации динамической привязки и вам нужен эквивалент частной виртуальной функции. То есть функция, которая вызывается из базового класса, но не обязательно должна быть частью открытого интерфейса class C. Вы можете сказать, ну, это не обязательно должно быть приватным, просто не позволяйте плебеям использовать его, что правда, но некоторые люди очень взволнованы, когда вы говорите им, что (кроме отключения копий по умолчанию) модификаторы доступа в С++ - пустая трата времени, и вы всегда должны делать все общедоступным ;-)   -  person Steve Jessop    schedule 26.10.2011
comment
@Nim: я бы хотел использовать CRTP, как упомянул Стив Джессоп.   -  person modjtabaf    schedule 03.11.2011
comment
@OliCharlesworth: он жалуется на неправильное использование ключевого слова друга, что-то вроде отсутствующего ключа класса.   -  person modjtabaf    schedule 03.11.2011
comment
@SteveJessop: я не могу понять, что вы имеете в виду, когда хорошо говорите, это не обязательно должно быть частным, просто не позволяйте плебсу использовать это. Как это можно сделать? используя ключевое слово «защищенное» вместо «частное»?   -  person modjtabaf    schedule 03.11.2011
comment
@modjtabaf: сделайте его общедоступным и напишите в документации (или в заголовочном файле), не вызывайте эту функцию, кроме как из базового класса CRTP. Тогда любой, кто его вызывает, находится в том же положении, что и тот, кто передает нулевой указатель на strlen - UB, и это их собственная вина, что не RTFM.   -  person Steve Jessop    schedule 04.11.2011


Ответы (1)


Он имеет неправильный формат и не является допустимым C++, хотя работает в MSVC. Стандарт С++ 03 говорит об этом (7.1.5.3 §2):

3.4.4 описывает, как происходит поиск имени для идентификатора в уточненном спецификаторе типа. Если идентификатор разрешается в имя-класса или имя-перечисления, уточненный-спецификатор-типа вводит его в объявление точно так же, как простой-спецификатор-типа вводит свое имя-типа. Если идентификатор разрешается в имя-определения-типа или в параметр-типа шаблона, спецификатор уточненного-типа имеет неправильный формат. [Примечание: это означает, что в шаблоне класса с параметром типа шаблона T объявление

       friend class T;

плохо сформирован. ] Если поиск имени не находит объявления для имени, уточненный спецификатор-типа имеет неправильный формат, если только он не имеет простой формы идентификатор ключа класса, и в этом случае идентификатор объявляется, как описано в 3.3.1.

По этой же причине вы также не можете делать такие вещи, как friend class std::string;, но вы должны подружить std::basic_string с параметрами шаблона.

Однако новая спецификация C++11 допускает новый синтаксис для объявления друзей, который просто (11.3 §3 N3242):

friend <typename-specifier>;

Этот новый синтаксис позволяет вам делать то, что вы хотите (хотя я не знаю, поддерживает ли это MSVC):

template<typename T>
class C : public T
{
    friend T;
};
person reko_t    schedule 26.10.2011