Есть некоторые изворотливые правила в отношении функций друзей.
namespace X {
template<class T>
struct A{
friend void foo(A<T>) {}
};
}
foo
выше не является функцией шаблона.Это не шаблонная дружественная функция, которая существует в пространстве имен, включающем A
, но ее можно найти только через поиск ADL;он не может быть непосредственно назван как X::foo
.
Так же, как члены шаблонов могут быть самими шаблонами, так и нет, это не-шаблонная функция друга, которая создается для каждого экземпляра класса шаблона A
.
namespace X{
template<class T>
void foo(A<T>);
}
Этот foo
является шаблоном функции с именем foo
в пространстве имен X
.Это не то же самое , что и у функции друга без шаблона foo
выше.
Из этого большинства ваших ошибок ясно.То, что вы считали предварительным объявлением, было несвязанной функцией шаблона.То, что вы считали другом, не было, поэтому у вас не было доступа к приватному конструктору.
Мы можем исправить это несколькими способами.Мой любимый способ - добавить тип тега.
template<class T>struct tag_t{using type=T;};
template<class T>
constexpr tag_t<T> tag{};
теперь мы можем использовать tag
для отправки ADL:
template<typename T>
struct fun {
using type = T;
friend fun bar(tag_t<fun>, foo<type> const& x, type y)
{ return {bar(x)+y}; }
private:
fun(type x) : X(x) {}
type X;
};
, а затем в основном это работает:
foo<int> x{42};
fun<int> y = bar(tag<fun<int>>, x, 7); // called here
Но вы, возможно, не захотите упоминать tag<fun<int>>
, поэтому мы просто создаем не-друга bar
, который звонит нам:
template<class T>
fun<T> bar(foo<T> const& x, type y)
{ return bar( tag<T>, x, y ); }
, и теперь ADL делает свою магию инайден правильный не шаблон bar
.
Другой подход заключается в том, чтобы сделать функцию template
bar
другом fun
.