此代码来自" C ++编程语言"作者:Bjarne Stroustrup(C.13.8.3实例化绑定点)
template <class T>
void f(T value)
{
g(value);
}
void g(int v);
void h()
{
extern g(double);
f(2);
}
他提到:
这里,f()的实例化点恰好在h()之前,所以 f()中调用的g()是全局g(int)而不是本地 克(双)。 “实例化点”的定义意味着a 模板参数永远不能绑定到本地名称或类 构件。
void h()
{
struct X {}; // local structure
std::vector<X> v; // error: can't use local structure as template parameter
}
我的问题是:
为什么第一个代码有效? g()
稍后被声明,我真的得到了G ++ 4.9.2的错误,g
当时没有声明。
extern g(double) - 这是如何运作的?因为在函数重载的情况下返回值并不重要,那么我们可以在前向声明中错过它吗?
f()的实例化点就在h()之前 - 为什么?在调用f(2)
时它会被实例化是不合逻辑的?就在我们称之为的地方,g(double)
已经在范围内了。
''实例化点'的定义意味着模板参数永远不能绑定到本地名称或类成员 - 这在C ++ 14中有变化吗?我在使用C ++时遇到错误(G ++ 4.9.2),但在C ++ 14(G ++ 4.9.2)中没有错误。
答案 0 :(得分:15)
“1985年,第一版的C ++编程语言发布,成为该语言的权威参考,因为还没有官方标准。” wiki C++ History因此它在C ++ 11和C ++ 14之间没有变化。我可以假设(并且请带着一点点盐)它在“预标准化”和标准化之间发生了变化。也许更了解C ++历史的人可以在这里发挥更多的作用。
至于实际发生的事情:
首先让我们摆脱简单的方式:
extern g(double);
这是无效的C ++。从历史上看,不幸的是C允许遗漏类型。在C ++中,您必须编写extern void g(double)
。
接下来,让我们忽略g(double)
重载来回答您的第一个问题:
template <class T>
void f(T value)
{
g(value);
}
void g(int v);
int main()
{
f(2);
}
在C ++中有臭名昭着的两阶段名称查找:
规则有点复杂,但这就是它的要点。
g
依赖于模板参数T
,因此它会通过第一阶段。这意味着如果您从未实例化f
,则代码编译得很好。在第二阶段f
使用T = int
进行实例化。现在搜索g(int)
,但未找到:
17 : error: call to function 'g' that is neither visible in the template definition nor found by argument-dependent lookup g(value); ^ 24 : note: in instantiation of function template specialization 'f<int>' requested here f(2); ^ 20 : note: 'g' should be declared prior to the call site void g(int v);
为了让任意名称g
通过飞扬的颜色,我们有几个选择:
g
:void g(int);
template <class T>
void f(T value)
{
g(value);
}
g
带入T
:template <class T>
void f(T)
{
T::g();
}
struct X {
static void g();
};
int main()
{
X x;
f(x);
}
g
T
:template <class T>
void f(T value)
{
g(value);
}
struct X {};
void g(X);
int main()
{
X x;
f(x);
}
这些当然会改变程序的语义。它们旨在说明您在模板中能够和不能拥有的内容。
至于为什么ADL找不到g(int)
,但找到g(X)
:
§3.4.2依赖于参数的名称查找[basic.lookup.argdep]
- 醇>
对于函数调用中的每个参数类型T,有一组零个或多个关联的命名空间以及一组零或多个 要考虑的相关类别:[...]:
如果T是基本类型,则其关联的命名空间和类集都是空的。
如果T是类类型(包括联合),则其关联的类是:类本身;它所属的成员,如果有的话;和 它的直接和间接基类。它的相关命名空间是 其关联类是成员的名称空间。 [...]
最后我们了解为什么找不到main中的extern void g(double);
:首先我们发现g(fundamental_type)
被找到iff它是在f
定义之前声明的。所以我们在void g(X)
内设main
。 ADL能找到吗?
template <class T>
void f(T value)
{
g(value);
}
struct X{};
int main()
{
X x;
void g(X);
f(x);
}
没有。因为它不与X
(即全局命名空间)位于同一名称空间中,所以ADL找不到它。
证明g
不在全球
int main()
{
void g(X);
X x;
g(x); // OK
::g(x); // ERROR
}
34:错误:全局命名空间中没有名为'g'的成员;你的意思是 只是'g'?