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 and exactly what's going on. Let me just jump straight to the code. This works:
template <typename T>
class Foo
{
struct Bar
{
Bar() {}
~Bar() noexcept {}
Bar(Bar&& b) : Bar() { swap(*this, b); }
friend void swap(Bar& b1, Bar& b2) { /* ... */ }
};
};
template class Foo<int>; // explicit instantiation of Foo with int type
But how do I move the definition of swap
outside of the Bar
struct body? If I do this:
template <typename T>
class Foo {
struct Bar {
// ...
Bar(Bar&& b) : Bar() { swap(*this, b); } // line 16
// ...
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) {} // line 26
template class Foo<int>; // line 31
g++ (4.7.1, flags: -Wall -std=c++11) reports:
main.cpp: In instantiation of ‘Foo<T>::Bar::Bar(Foo<T>::Bar&&)
[with T = int; Foo<T>::Bar = Foo<int>::Bar]’:
main.cpp:31:16: required from here
main.cpp:16:28: error: no matching function for call to
‘swap(Foo<int>::Bar&, Foo<int>::Bar&)’
main.cpp:16:28: note: candidate is:
main.cpp:26:6: note: template<class T> void swap(typename Foo<T>::Bar&,
typename Foo<T>::Bar&)
main.cpp:26:6: note: template argument deduction/substitution failed:
main.cpp:16:28: note: couldn't deduce template parameter ‘T’
I guess the code for swap
also needs to be created when explicitly instantiating Foo
, which makes sense, but why can't the compiler figure out that swap(Foo<int>::Bar&...)
needs to be created? Why does the template substitution fail? Or have I got everything wrong?
UPDATE 1
With:
template <typename T> class Foo;
template <typename T>
void swap(typename Foo<T>::Bar& b1, typename Foo<T>::Bar& b2);
template <typename T>
class Foo {
struct Bar {
Bar(Bar&& b) : Bar() { swap(*this, b); } // line 19
friend void swap<>(Foo<T>::Bar& b1, Foo<T>::Bar& b2); // line 20
};
};
template <typename T>
void swap(typename Foo<T>::Bar& b1, typename Foo<T>::Bar& b2) {} // line 26
template class Foo<int>; // line 29
g++ (4.7.1, flags: -Wall -std=c++11) reports:
main.cpp: In instantiation of ‘struct Foo<int>::Bar’:
main.cpp:29:16: required from here
main.cpp:20:17: error: template-id ‘swap<>’ for ‘void swap(Foo<int>::Bar&, Foo<int>::Bar&)’ does not match any template declaration
main.cpp: In instantiation of ‘Foo<T>::Bar::Bar(Foo<T>::Bar&&) [with T = int; Foo<T>::Bar = Foo<int>::Bar]’:
main.cpp:29:16: required from here
main.cpp:19:24: error: no matching function for call to ‘Foo<int>::Bar::Bar()’
main.cpp:19:24: note: candidate is:
main.cpp:19:5: note: Foo<T>::Bar::Bar(Foo<T>::Bar&&) [with T = int; Foo<T>::Bar = Foo<int>::Bar]
main.cpp:19:5: note: candidate expects 1 argument, 0 provided
main.cpp:19:28: error: no matching function for call to ‘swap(Foo<int>::Bar&, Foo<int>::Bar&)’
main.cpp:19:28: note: candidate is:
main.cpp:26:8: note: template<class T> void swap(typename Foo<T>::Bar&, typename Foo<T>::Bar&)
main.cpp:26:8: note: template argument deduction/substitution failed:
main.cpp:19:28: note: couldn't deduce template parameter ‘T’
UPDATE 2
OK, so this can't be done. Piotr has linked to Output a nested class inside a template, but I don't understand the answer. Why can't swap
be defined outside its declaration? As far as I (mis)understand things, why can't the compiler create code for swap(Foo<int>::Bar&...)
and link to it in the code for the explicit instantiation of Foo<int>
? Have I totally misunderstood what's going on? What's the problem?
UPDATE 3
OK, this can't be done because if there are template specializations the compiler can't guarantee calls to swap
defined outside of Foo
are unambiguous since Foo<some_class>::Bar
might be something completely different in a particular specialization. I hope I've got that right. But, why doesn't g++ warn me about this before I create an explicit instantiation of Foo
?
template <typename T>
class Foo {
struct Bar {
// ...
Bar(Bar&& b) : Bar() { swap(*this, b); }
// ...
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>; // let's comment this explicit instantiation out.
This code compiles fine (g++ 4.7.1, flags: -Wall -std=c++11). But, shouldn't it warn me that this code could possibly cause problems? When I add the explicit instantiation of Foo
, the problem isn't with that line itself, but with the swap
code implemented outside of Foo
.