更新时间:2022-12-24 19:31:58
问题不在于 friend
。
问题是这个函数本身:
template< typename T>
void swap(typename Foo< T> :: Bar& b1,typename Foo< T> :: Bar& b2){} // line 26
无法从嵌套类中取得模板参数 T
,请参阅:在模板中输出嵌套类甚至更好这个答案: http://***.com/a/4092248/1463922
为了说明为什么无法做到这一点,考虑这个函数:
template< class T>
void foo(typename A< T> :: Bar);
这个定义:
template< class T>
struct A {typedef int Bar; };
此电话:
int a;
foo(a);
在这个例子中 T
是什么?是 int
,因为 A< int> :: Bar
是 int $ c $因为
A< float> :: Bar
是问题是你调用
或... foo< int>(int)
或 foo< float>(int)
或者让示例更接近问题:
模板< class T>
struct Foo {
struct Bar {};
};
似乎编译器应该没有解决问题的方法:
模板< class T>
void resolve(typename Foo< T> :: Bar *);
但编译器甚至在这里有问题,因为它不能确定一些其他类的专门化不会使用inner其他类的struct,例如:
template< class T&
struct Foo< T *> {
typedef Foo< T> :: Bar Bar;
};
因此:
Foo< void> :: Bar b;
resolve(& b);
编译器无法知道调用哪个版本:
解析< void>(Foo< void> :: Bar *);
//或
resolve< void *>(Foo< void> :: Bar *);
// ^
建议你 - 使用内联的朋友 - 但实现它与一些其他模板类。这工作 - 但我相信这是一个有点过度设计:
模板< class S&
类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不是inline ....
}
我做Bar公开做这个测试:
Foo< int> :: Bar a = Foo< int> :: Bar // move constructor
int main(){
swap(a,a); // explicit swap
}
[OLD]
我以前的答案是完全错误的,第一个评论指它。
friend void swap<>(typename Foo< T> :: Bar& typename Foo< T& );
// ^^
[/ OLD]
/ p>
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
.
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: http://***.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]