Наверное, раньше задавали вопрос, но все это приближается к пределу моего понимания и понимания С++, поэтому я немного замешкаюсь, понимая, о чем говорят и что именно происходит. Позвольте мне просто перейти прямо к коду. Это работает:
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
Но как перенести определение swap
вне тела структуры Bar
? Если я это сделаю:
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 = С++ 11) сообщает:
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’
Я предполагаю, что код для swap
также должен быть создан при явном экземпляре Foo
, что имеет смысл, но почему компилятор не может определить, что swap(Foo<int>::Bar&...)
необходимо создать? Почему смена шаблона не выполняется? Или у меня все не так?
ОБНОВЛЕНИЕ 1
С
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 = С++ 11) сообщает:
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’
ОБНОВЛЕНИЕ 2
ОК, так что это невозможно. Piotr связал с вывод вложенного класса внутри шаблона, но я не понимаю ответа. Почему не может быть swap
определено вне его объявления? Насколько я (неправильно) понимаю вещи, почему компилятор не может создать код для swap(Foo<int>::Bar&...)
и ссылаться на него в коде для явного создания Foo<int>
? Неужели я совершенно не понял, что происходит? В чем проблема?
ОБНОВЛЕНИЕ 3
ОК, это невозможно сделать, потому что, если существуют специализированные шаблоны, компилятор не может гарантировать, что вызовы swap
, определенные вне Foo
, недвусмысленны, поскольку Foo<some_class>::Bar
может быть чем-то совершенно иным в конкретной специализации. Надеюсь, я прав. Но почему g++ не предупреждает об этом, прежде чем я создам явное создание 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 comment this explicit instantiation out.
Этот код компилирует fine (g++ 4.7.1, flags: -Wall -std = С++ 11). Но не следует ли мне предупреждать меня, что этот код может вызвать проблемы? Когда я добавляю явное создание Foo
, проблема заключается не в самой строке, а в коде swap
, реализованном вне Foo
.