我一直在试验Sean Parent的“C ++ Seasoning”演示文稿中的代码,并将我的问题归结为以下代码:
#include <memory>
struct container {
struct concept {
virtual ~concept() {}
virtual void foo_() = 0;
};
template <class T> struct model : concept {
model (T x) : data_(x) {}
void foo_() {
foo(data_); // Line 13
}
T data_;
};
template <class T>
container(T x) : self_(new model<T>(x)) {} // Line 20
std::unique_ptr<concept> self_;
friend void foo(container &c) { c.self_->foo_(); }
};
void foo(int i) // Line 27
{
}
int main()
{
int i = 5;
container c(i); // Line 34
foo(c);
}
我遇到的问题是这个代码用g ++编译,而不是用Clang编译。
Clang给出了以下错误消息:
prio.cpp:13:13: error: call to function 'foo' that is neither visible in the
template definition nor found by argument-dependent lookup
foo(data_);
^
prio.cpp:20:32: note: in instantiation of member function
'container::model<int>::foo_' requested here
container(T x) : self_(new model<T>(x)) {}
^
prio.cpp:34:15: note: in instantiation of function template specialization
'container::container<int>' requested here
container c(i);
^
prio.cpp:27:6: note: 'foo' should be declared prior to the call site
void foo(int i)
^
我的理解是模板期间的重载解析发生在实例化时。在这种情况下,即第34行(如上所示)。此时,全局“foo”函数是已知的。然而,似乎没有解决。
后人的注意事项:这是Clang于14 / Jan / 14开始建造的
这是Clang中的错误,还是使用g ++?
答案 0 :(得分:2)
在这种情况下Gcc是错误的,代码应该不编译;但这与模板完全无关。朋友声明的特别之处在于它们为命名空间级实体提供声明,但是在编译器也看到命名空间声明之前,声明不是可见用于正常查找。
考虑简化的例子:
struct X {
friend void f(int); // [1]
void g() { f(1); } // [2]
};
void h() { f(1); } // [3]
void f(int); // [4]
void i() { f(1); } // [5]
X
类中的友元声明[1]为命名空间级别函数f
提供声明,其中int
,但该声明不是在命名空间级别可见,直到[4]中存在命名空间级别声明。 [2]和[3]都将无法编译,尽管[5]将编译,因为此时编译器将解析函数声明。
那么编译器如何使用[1]中的声明来解析一个调用呢?在这种特殊情况下,永远不会。友元声明只能通过参数依赖查找找到,但如果函数调用的其中一个参数类型为X
,则ADL只会查看X
内部。在这种情况下,该函数没有任何参数X
,因此查找将永远不会使用friend
声明,除了解除对{的变量的访问限制{1}}。
那是:
X
如果struct Y {
friend void f(int) {}
};
没有后面的命名空间级别声明,则会声明并定义一个不能在程序中的任何地方使用的函数(查找将永远找不到它)。
您的问题的简单解决方法是在定义类之前在命名空间级别为函数提供声明:
f