Probably been asked before, but all this is approaching the limit of my comprehension and cognizance of C++, so I\'m a little slow in understanding what\'s being talked about an
The problem is not with friend
.
The problem is with this function itself:
template <typename T>
void swap(typename Foo<T>::Bar& b1, typename Foo<T>::Bar& b2) {} // line 26
Deducing template parameter T
from nested class is not possible, see: Output a nested class inside a template or even better this answer: https://stackoverflow.com/a/4092248/1463922
To give example why it cannot be done, consider this function:
template <class T>
void foo(typename A<T>::Bar);
And this A definition:
template <class T>
struct A { typedef int Bar; };
And this call:
int a;
foo(a);
What is T
in this example? Is it int
because A<int>::Bar
is int
, OR float
because
A<float>::Bar
is int
too OR whatever you want.... The question is what function you are calling foo<int>(int)
or foo<float>(int)
or ...
Or to give example more closer to question:
template <class T>
struct Foo {
struct Bar {};
};
It seems that compiler should have no problems with resolving this:
template <class T>
void resolve(typename Foo<T>::Bar*);
But compiler even here has problems because it is not sure if specialization of some other class will not use inner struct of other class, like this:
template <class T>
struct Foo<T*> {
typedef Foo<T>::Bar Bar;
};
So for:
Foo<void>::Bar b;
resolve(&b);
Compiler has no chance to know which version to call:
resolve<void>(Foo<void>::Bar*);
// or
resolve<void*>(Foo<void>::Bar*);
// ^
What can I advice you - use inline friend - but implement it with some other template class. This works - but I am sure this is a little over-engineered:
template <class S>
class ImplementSwap;
template <typename T>
class Foo {
public:
struct Bar {
int a;
Bar() {}
~Bar() {}
friend class ImplementSwap<Foo<T>>;
friend void swap(Foo<T>::Bar& b1, Foo<T>::Bar& b2)
{ ImplementSwap<Foo<T>>::doSwap(b1, b2); }
Bar(Bar&& b) { swap(*this, b); }
};
};
template <class T>
class ImplementSwap<Foo<T>> {
public:
static void doSwap(typename Foo<T>::Bar&,typename Foo<T>::Bar&);
};
template <class T>
void ImplementSwap<Foo<T>>::doSwap(typename Foo<T>::Bar&,typename Foo<T>::Bar&)
{
// this one is not inline....
}
I made Bar public to do this test:
Foo<int>::Bar a = Foo<int>::Bar(); // move constructor
int main() {
swap(a,a); // explicit swap
}
[OLD]
My previous answer was completely wrong, and first comments refer to it.
friend void swap<>(typename Foo<T>::Bar&, typename Foo<T>::Bar&);
// ^^
[/OLD]
For me this should look like this:
template <typename T>
class Foo
{
struct Bar
{
template <typename V>
friend void swap(typename Foo<V>::Bar&, typename Foo<V>::Bar&);
};
};
template <typename T>
void swap(typename Foo<T>::Bar& b1, typename Foo<T>::Bar& b2) { }
template class Foo<int>;
int main()
{
Foo<int>::Bar bb1, bb2;
swap<int>(bb1, bb2);
}
This runs with gcc: http://ideone.com/zZMYn
Few notes:
Friend declaration inside the class creates forward declaration of the object that is declared as friend outside of the class. This applies to friend classes, functions, templates. This means that forwards decl of your swap
is not needed.
Syntax some-name<..>
is used in template specializations and template instantiations. It is not used in forward declarations (including friend declarations) and definitions. In your example you have one forward declaration, one definition and one call (that is an instantiation for templates of the functions).
If you call the function swap as swap(bb1, bb2);
, compiler cannot find it. For me the need to call this function as in the example above swap<int>
is more a compiler problem rather than a language requirement. Compiler should deduce template params for template function calls.
EDIT
In the example above the vars bb1
and bb2
have type Foo<int>::Bar
. This means that the instantiation of swap
should be:
void swap(Foo<int>::Bar &b1, Foo<int>::Bar &b2) { }
No any other instantiation can work here because say Foo<float>::Bar
is a different type from Foo<int>::Bar
. There is no way to convert Foo<int>::Bar
into Foo<float>::Bar
. Event if template for Foo<float>::Bar
would be instantitated, it cannot be used. The types of params are different.
If there would be several specializations for this template around, the situation might be more complicated. But at the point of the call there is nothing than the template itself. To be considered, specialization should be visible at the point of the call.
Excellent compiler might be able to handle this case. Since the work around with explictit type specification is available and seems to work, I would say that the current state of gcc fully Ok.