Как определить дружественную функцию шаблона класса шаблона вне его объявления?

Если я определю функцию-шаблон друга в объявлении класса шаблона следующим образом, он может скомпилироваться.

#include <iostream>

template <typename T>
class X {
public:
X(int i) : n(i) {}

private:
T n;

template <typename U>
friend void doStuff(const X<T>& x, const U& u)
{
std::cout << (x.n + u) << std::endl;
}
};

int main()
{
X<int> x(1);
doStuff(x, 3);
return 0;
}

Но если я перейду определение doStuff() вне класса декалерация, сразу после объявления класса X<>, как следует, это не может скомпилировать.

template <typename U>
template <typename T>
void doStuff(const X<T>& x, const U& u)
{
std::cout << (x.n + u) << std::endl;
}

Ни один не делает следующий код.

template <typename U, typename T>
void doStuff(const X<T>& x, const U& u)
{
std::cout << (x.n + u) << std::endl;
}

Итак, как я должен определить функцию шаблона doStuff() вне объявления класса?

Заранее спасибо.

1

Решение

Возможно, вот так:

template <typename T>
class X {
public:
X(int i) : n(i) {}

private:
T n;

template <typename U, typename V>
friend void doStuff(const X<U>& x, const V& u);
};

template <typename U, typename V>
void doStuff(const X<U>& x, const V& u)
{
std::cout << (x.n + u) << std::endl;
}

int main()
{
X<int> x(1);
doStuff(x, 3);
X<double> y(1.0);
doStuff(y, 3.5);
}
5

Другие решения

@jrok дает хорошее решение.

Давайте уточним, почему это работает, и почему это необходимо.

Объявляя функцию как

template <typename U, typename V>
friend void doStuff(const X<U>& x, const V& u);

Вы говорите, что есть функция doStuff для каждого X и V. Но вы тогда спрашиваете, что если U не T? Хорошо, когда вы вызываете doStuff с аргументом типа X, int, единственный приемлемый результат вывода аргумента шаблона — это установить тип T определения класса в int.

Теперь, почему это не работает, просто сделать это:

template <typename V>
friend void doStuff(const X<T>& x, const V& u);

?

На самом деле, это вроде как. Но это означает, что для любого типа T вы говорите компилятору, что вы собираются определять функция, которая принимает X<T>для любого необходимого Tи где второй параметр является параметром шаблона. Обратите внимание, что первый аргумент не является параметром функции шаблона.

Там нет никакого способа обобщенно говорят «всякий раз, когда пользователь использует класс типа X<T>использовать этот конкретный образец для того, как определять функция doStuff где X<T> это первый параметр.

Итак, в классе вы пообещали, что оно будет существовать, но нет способа написать определение в шаблонной форме. это должно быть что-то вроде

template <typename T,typename U>
for_any_seen_template_instance_anywhere_in_the_program_< X<T> >::
void doStuff(const X<U>& x, const V& u){
... code.
}

Нет такого понятия. Еще один способ думать об этом: когда вы пишете

doStuff(a,b);

если первый параметр не является параметром шаблона функции, автоматически ничего не генерируется относительно этого первого аргумента. Типы параметров шаблона шаблонов классов не выводятся автоматически, как параметры функции шаблона.

Тем не менее, вы Можно определять X<T> для любого необходимого T (как вы обещали в объявлении), но вы должны сделать это самостоятельно для каждого требуемого типа:

template<typename V>
void doStuff(const X<int>& x, const V& u){
std::cout << (x.n + u) << std::endl;
}

РЕДАКТИРОВАТЬ, отвечая на комментарий ниже.

Это правда, что все doStuff(X<U>,int) например, друзья любого X<T> независимо от типов T а также U, Это означает, что внутри тела doStuff вы можете получить доступ к частным объектам типа X<Q>, В этом случае вы не передавали такие объекты в функцию, но вы могли сделать X<std::string> там и поиграть с его рядовыми. Это цена, которую вы платите за то, что не пользовались встроенным устройством, которое было у вас изначально.

3

По вопросам рекламы [email protected]