Различные имена типа в объявлении и определении шаблонной функции

220
02 августа 2017, 21:29

Недавно столкнулся с такой проблемой как осложнение operator<< для template class, и вот ищя ответ на этот вопрос, перерыл много вопросов на английском stack'e. Нашёл решение вот тут. Меня смутило такое решение проблемы -

template <typename T>
class Test {
   template <typename U>      // all instantiations of this template are my friends
   friend std::ostream& operator<<( std::ostream&, const Test<U>& );
};
template <typename T>
std::ostream& operator<<( std::ostream& o, const Test<T>& ) {
   // Can access all Test<int>, Test<double>... regardless of what T is
}

Я не понимаю почему тут используется совсем другой template - template <typename U>, а тут такой template <typename T>.

Вроде как пытался перевести, но так и не понял. А может я не совсем понимаю работы friend?

Answer 1

На самом деле нет никакой разницы между template <typename T>, template <typename U>, template <class A> и template <class BestTypeEver>. Это просто псевдоним для типа, который будет выведен компилятором во время компиляции. Так что можете заменить эти имена так чтобы они вас не смущали:

template <typename T>
class Test {
   template <typename U>      // all instantiations of this template are my friends
   friend std::ostream& operator<<( std::ostream&, const Test<U>& );
};
template <typename U>
std::ostream& operator<<( std::ostream& o, const Test<U>& ) {
   // Can access all Test<int>, Test<double>... regardless of what U is
}

Что касается friend, это ключевое слово говорит то том, что ваш оператор << может обращаться к приватным и защищенным членам класса.

class A{
    int _privateMember;
    friend std::ostream& operator<<( std::ostream& o, const A& a);
}
std::ostream& operator<<( std::ostream& o, const A& a){
    o << a._privateMember; //так можно только благодаря friend 
    return o;
}

Строго говоря, если вы можете добраться до всего что вам нужно через открытый интерфейс, то можно обойтись и без friend:

class A{
    int _privateMember;
public:
    int getPrivateMember() const{
        return _privateMember;
    }
}
std::ostream& operator<<( std::ostream& o, const A& a){
    o << a.getPrivateMember();
    return o;
}
Answer 2

Во-первых, как вам же ответили, шаблонный параметр - это лишь локальный формальный параметр, имя которого не имеет никакого значения. В точке friend-объявления оператора вы не можете использовать имя T, так как оно уже "занято" охватывающим шаблоном. А в точке определения оператора имя T свободно и его можно использовать.

Однако, во-вторых, стоит заметить, что в данном случае вы наблюдаете то, что можно назвать "ленивым" вариантом friend-объявления. Этот вариант более компактен, но в то же время формально избыточен, ибо он объявляет все специализации оператора << друзьями всех специализаций класса Test. В частности operator << <int> будет являться другом класса Test<double>. (Именно об этом написано в комментарии внутри определения оператора.)

В такой избыточной "перекрестной дружбе" нет ничего плохого или опасного, однако если она вам не нужна и вы хотите поступить более педантично, т.е сделать так, чтобы каждая специализация оператора дружила именно и только со "своей" специализацией шаблона класса Test, то писанины будет чуть побольше

template <typename> 
class Test;
template <typename T> 
std::ostream& operator<<( std::ostream& o, const Test<T>& );
template <typename T>
class Test {
   friend std::ostream& operator<< <>( std::ostream&, const Test& );
};
template <typename T>
std::ostream& operator<<( std::ostream& o, const Test<T>& ) {
   // Can access only Test<T>
}

Именно из-за несколько большей громоздкости такого педантичного варианта зачастую некоторые авторы предпочитают прибегать к "ленивому" варианту.

READ ALSO
zero-initialization и memset в конструкторе

zero-initialization и memset в конструкторе

Аналогичны ли два этих конструктора?

258
Бряк в visual studio при удалениии массива

Бряк в visual studio при удалениии массива

В целом, я даже не уверен ещё правильно ли освобождаю память, и освобождается ли она вообщено на запуске дебаггер ругается именно на этот...

244
Не могу добавить в вектор умный указатель

Не могу добавить в вектор умный указатель

Имеется класс MyClass, разумеется с конструктором, нужно создать вектор умных указателей на объекты этого классаСам указатель создается, но при...

213
Отображение контента по кнопка jquery

Отображение контента по кнопка jquery

ПриветствуюСверстал страницу на bootstrap'e

249