vso*_*tco 15 c++ templates static-polymorphism
我正在玩静态多态,我正在调用一个函数,它根据初始参数的类型在内部调用"正确"的专用函数(基本上我正在做标记).这是代码:
#include <iostream>
using namespace std;
// tags
struct tag1{};
struct tag2{};
// the compliant types, all should typedef tag_type
struct my_type1
{
using tag_type = tag1;
};
struct my_type2
{
using tag_type = tag2;
};
// static dispatch via tagging
template <typename T>
void f(T)
{
cout << "In void f<typename T>(T)" << endl;
// why can I call f_helper without forward definition?!?
f_helper(typename T::tag_type{});
}
int main()
{
my_type1 type1;
my_type2 type2;
// how does f below knows about f_helper ?!?!
// even after instantiation f_helper shouldn't be visible!
f(type1);
f(type2);
}
// helper functions
void f_helper(tag1)
{
cout << "f called with my_type1" << endl;
}
void f_helper(tag2)
{
cout << "f called with my_type2" << endl;
}
Run Code Online (Sandbox Code Playgroud)
因此,f(T)使用参数调用my_type1或my_type2内部必须tag_type使用适当的标记tag1/ 来键入tag2.根据这个内部tag_type,然后调用"正确"的包装器,这个决定当然是在编译时做出的.现在我真的不明白为什么这个代码工作?为什么我们不需要前向声明f_helper?我首先在之前main(和之后f)定义了包装器,我认为这是有道理的,你不需要转发声明,因为编译器只在f(type1);被调用(in main())之前实例化模板,之后它才知道类型T,所以在实例化时编译器知道f_wrapper.
但是正如你所看到的,即使我声明包装器AFTER main(),代码仍然有效.为什么会这样?我想问题有点奇怪,问为什么代码有效:)
编辑
即使在gcc5和gcc HEAD 6.0.0中,代码仍继续编译.
f_helper(typename T::tag_type{})是一个依赖于类型的表达式,因为它T::tag_type是一个依赖类型.这意味着在由于两阶段查找而实例化f_helper之前不需要可见f<T>.
编辑:我很确定这实际上是未定义的行为.如果我们看一下14.6.4.2 [temp.dep.candidate],我们会看到这段话:
对于依赖于模板参数的函数调用,使用通常的查找规则(3.4.1,3.4.2,3.4.3)找到候选函数,除了:
- 对于使用非限定名称查找(3.4.1)或限定名称查找(3.4.3)的查找部分,仅找到模板定义上下文中的函数声明.
- 对于使用关联命名空间(3.4.2)的查找部分,仅找到在模板定义上下文或模板实例化上下文中找到的函数声明.
如果函数名称是非限定id并且调用将是格式错误或者找到更好的匹配,则相关命名空间内的查找会考虑所有在所有翻译单元中的那些名称空间中引入外部链接的函数声明,而不仅仅是考虑在模板定义和模板实例化上下文中找到的那些声明,则程序具有未定义的行为.
我的最后一段表明这是未定义的行为.这function call that depends on a template parameter是f_helper(typename T::tag_type{}).f_helper在f实例化时不可见,但是如果我们在编译完所有翻译单元后执行名称查找.