在定义模板后声明函数
Declare function after template defined
假设我有一个模板函数:
template <class T>
void tfoo( T t )
{
foo( t );
}
稍后我想将它与类型一起使用,所以我 declare/define 一个函数并尝试调用它:
void foo( int );
int main()
{
tfoo(1);
}
我从 g++ 收到错误:
‘foo’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
foo( t );
为什么在实例化时找不到void foo(int)
?它是在那个时候宣布的。有没有办法让它工作(无需在模板之前移动 foo
的声明)?
foo
在您的例子中是一个 从属名称 ,因为如果参数和参数类型取决于模板参数,则函数选择取决于类型。也就是说foo
是按照依赖查找.
的规则查找的
dependent 和 non-dependent 查找之间的区别在于,在依赖查找的情况下 ADL 命名空间 被视为 extended:它们使用从模板实例化点可见的额外名称进行扩展(在您的情况下为 tfoo
调用)。这包括出现在模板声明之后的名称。这里的关键点是只有 ADL 指定的命名空间以这种方式扩展。
(通过 ADL 指定的命名空间 我指的是与 函数参数类型相关联的命名空间 ,因此被相关规则考虑名称查找。请参阅“3.4.2 参数相关名称查找”)
在您的例子中,参数的类型为 int
。 int
是基本类型。基本类型没有关联的命名空间(参见“3.4.2 Argument-dependent name lookup”),这意味着它不通过 ADL 命名任何命名空间。在您的示例中,根本不涉及 ADL。 foo
的从属名称查找在这种情况下与非从属查找没有区别。它将无法看到您的 foo
,因为它是在模板下方 下声明的。
注意与下面例子的区别
template <class T> void tfoo( T t )
{
foo( t );
}
struct S {};
void foo(S s) {}
int main()
{
S s;
tfoo(s);
}
此代码将编译,因为参数类型 S
是 class 类型。它有一个关联的命名空间——全局命名空间——它添加(指定)该全局命名空间以进行相关名称查找。这样的 ADL 命名空间可以通过依赖查找以 updated 形式看到(从调用点看)。这就是查找可以看到 foo
并成功完成的原因。
人们认为所谓的"two-phase lookup"的第二阶段应该能够看到一切[=51],这是一个相当普遍的误解=] 在模板定义下方额外声明,一直到实例化点(在本例中为调用点)。
不对,第二阶段不看一切。它只能在与函数参数相关联的名称空间中看到额外的内容。所有其他名称空间都不会更新。它们被看作是从模板定义的角度观察到的。
假设我有一个模板函数:
template <class T>
void tfoo( T t )
{
foo( t );
}
稍后我想将它与类型一起使用,所以我 declare/define 一个函数并尝试调用它:
void foo( int );
int main()
{
tfoo(1);
}
我从 g++ 收到错误:
‘foo’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive] foo( t );
为什么在实例化时找不到void foo(int)
?它是在那个时候宣布的。有没有办法让它工作(无需在模板之前移动 foo
的声明)?
foo
在您的例子中是一个 从属名称 ,因为如果参数和参数类型取决于模板参数,则函数选择取决于类型。也就是说foo
是按照依赖查找.
dependent 和 non-dependent 查找之间的区别在于,在依赖查找的情况下 ADL 命名空间 被视为 extended:它们使用从模板实例化点可见的额外名称进行扩展(在您的情况下为 tfoo
调用)。这包括出现在模板声明之后的名称。这里的关键点是只有 ADL 指定的命名空间以这种方式扩展。
(通过 ADL 指定的命名空间 我指的是与 函数参数类型相关联的命名空间 ,因此被相关规则考虑名称查找。请参阅“3.4.2 参数相关名称查找”)
在您的例子中,参数的类型为 int
。 int
是基本类型。基本类型没有关联的命名空间(参见“3.4.2 Argument-dependent name lookup”),这意味着它不通过 ADL 命名任何命名空间。在您的示例中,根本不涉及 ADL。 foo
的从属名称查找在这种情况下与非从属查找没有区别。它将无法看到您的 foo
,因为它是在模板下方 下声明的。
注意与下面例子的区别
template <class T> void tfoo( T t )
{
foo( t );
}
struct S {};
void foo(S s) {}
int main()
{
S s;
tfoo(s);
}
此代码将编译,因为参数类型 S
是 class 类型。它有一个关联的命名空间——全局命名空间——它添加(指定)该全局命名空间以进行相关名称查找。这样的 ADL 命名空间可以通过依赖查找以 updated 形式看到(从调用点看)。这就是查找可以看到 foo
并成功完成的原因。
人们认为所谓的"two-phase lookup"的第二阶段应该能够看到一切[=51],这是一个相当普遍的误解=] 在模板定义下方额外声明,一直到实例化点(在本例中为调用点)。
不对,第二阶段不看一切。它只能在与函数参数相关联的名称空间中看到额外的内容。所有其他名称空间都不会更新。它们被看作是从模板定义的角度观察到的。