之前可能会被问过,但所有这些都接近了我理解和认识C ++的极限,所以我在理解被讨论的内容以及究竟发生了什么方面有点慢。让我直接跳到代码。这有效:
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 = c ++ 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 = c ++ 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 已链接到Output a nested class inside a template,但我不明白答案。为什么不能在声明之外定义swap
?至于我(错误)理解的事情,为什么编译器不能为swap(Foo<int>::Bar&...)
创建代码并在代码中链接到Foo<int>
的显式实例化?我完全误解了发生了什么事吗?有什么问题?
更新3
好的,这是无法做到的,因为如果存在模板特化,编译器无法保证在swap
之外定义的Foo
的调用是明确的,因为Foo<some_class>::Bar
可能是完全的在特定的专业化中有所不同。我希望我做对了。但是,为什么在我创建Foo
的显式实例化之前g ++没有警告过我?
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.
此代码编译正常(g ++ 4.7.1,flags:-Wall -std = c ++ 11)。但是,它不应该警告我这个代码可能会导致问题吗?当我添加Foo
的显式实例化时,问题不在于该行本身,而是在swap
之外实现Foo
代码。
答案 0 :(得分:3)
问题不在于friend
。
问题在于这个功能本身:
template <typename T>
void swap(typename Foo<T>::Bar& b1, typename Foo<T>::Bar& b2) {} // line 26
无法从嵌套类中推导模板参数T
,请参阅:Output a nested class inside a template或甚至更好的答案:https://stackoverflow.com/a/4092248/1463922
举例说明为什么不能这样做,请考虑这个功能:
template <class T>
void foo(typename A<T>::Bar);
这个A定义:
template <class T>
struct A { typedef int Bar; };
这个电话:
int a;
foo(a);
此示例中的T
是什么?是int
,因为A<int>::Bar
是int
,还是float
因为
A<float>::Bar
也是int
或者你想要的......问题是你打电话给foo<int>(int)
或foo<float>(int)
或者......
或者举个更接近问题的例子:
template <class T>
struct Foo {
struct Bar {};
};
似乎编译器在解决此问题时应该没有问题:
template <class T>
void resolve(typename Foo<T>::Bar*);
但是编译器甚至存在问题,因为它不确定某些其他类的特化是否会使用其他类的内部结构,如下所示:
template <class T>
struct Foo<T*> {
typedef Foo<T>::Bar Bar;
};
所以:
Foo<void>::Bar b;
resolve(&b);
编译器无法知道要调用的版本:
resolve<void>(Foo<void>::Bar*);
// or
resolve<void*>(Foo<void>::Bar*);
// ^
我可以为您提供哪些建议 - 使用内联好友 - 但可以使用其他模板类实现它。这有效 - 但我确信这有点过度设计:
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....
}
我让Bar公开做这个测试:
Foo<int>::Bar a = Foo<int>::Bar(); // move constructor
int main() {
swap(a,a); // explicit swap
}
<击> [旧] 我之前的回答是完全错误的,并且第一条评论指的是它。 击>
<击>friend void swap<>(typename Foo<T>::Bar&, typename Foo<T>::Bar&);
// ^^
击> <击> [/ OLD] 击>
答案 1 :(得分:1)
对我来说,这应该是这样的:
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);
}
这与gcc:http://ideone.com/zZMYn
一起运行很少注意到:
类中的Friend声明创建在类之外声明为friend的对象的前向声明。这适用于朋友类,函数,模板。这意味着您不需要swap
的前锋。
语法some-name<..>
用于模板特化和模板实例化。它不用于前向声明(包括好友声明)和定义。在您的示例中,您有一个前向声明,一个定义和一个调用(这是函数模板的实例化)。
如果将函数swap称为swap(bb1, bb2);
,则编译器无法找到它。对我来说,需要像上面的示例中那样调用此函数swap<int>
更多的是编译器问题而不是语言要求。编译器应该为模板函数调用推导出模板参数。
修改强>
在上面的示例中,变量bb1
和bb2
的类型为Foo<int>::Bar
。这意味着swap
的实例化应该是:
void swap(Foo<int>::Bar &b1, Foo<int>::Bar &b2) { }
此处没有任何其他实例可以使用,因为Foo<float>::Bar
与Foo<int>::Bar
的类型不同。无法将Foo<int>::Bar
转换为Foo<float>::Bar
。如果Foo<float>::Bar
的模板将立即生效,则无法使用。参数类型不同。
如果这个模板有几个专业化,情况可能会更复杂。但是在通话时,没有什么比模板本身更重要了。需要考虑的是,专业化应该在通话时可见。
优秀的编译器可能能够处理这种情况。由于可以使用explictit类型规范并且似乎有效,我会说gcc的当前状态完全可以。