[英]At which point occurs template Instantiation binding?
此代码来自Bjarne Stroustrup的“C ++编程语言”(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)而不是局部g(double)。 “实例化点”的定义意味着模板参数永远不能绑定到本地名称或类成员。
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)中没有出错。
“1985年,第一版的C ++编程语言发布,成为该语言的权威参考,因为还没有官方标准 。” wiki C ++历史因此它在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
经由ADL: 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是类类型(包括联合),则其关联的类是:类本身; 它所属的成员,如果有的话; 及其直接和间接基类。 其关联的名称空间是其关联类是成员的名称空间。 [...]
最后我们得出为什么extern void g(double);
找不到内部主要内容:首先,我们发现如果在f
定义之前声明了g(fundamental_type)
则会找到它。 所以让我们在main
里面使它成为void g(X)
。 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'吗?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.