C++ 为什么在通过模板进行静态调度时不需要转发声明?
我正在玩一点静态多态性,我正在调用一个函数,该函数根据初始参数的类型在内部调用“正确的”专用函数(基本上我在做标记)。代码如下:C++ 为什么在通过模板进行静态调度时不需要转发声明?,c++,templates,static-polymorphism,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
#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;
}
#包括
使用名称空间std;
//标签
结构tag1{};
结构tag2{};
//兼容类型,均应为typedef tag_type
结构我的类型1
{
使用tag_type=tag1;
};
结构我的_类型2
{
使用tag_type=tag2;
};
//通过标记进行静态调度
模板
空隙f(T)
{
coutf_-helper(typename T::tag_-type{})
是一个类型相关的表达式,因为T::tag_-type
是一个依赖类型。这意味着f_-helper
在由于两阶段查找而实例化之前不需要可见
编辑:我很确定这实际上是一种未定义的行为。如果我们看一下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,调用的格式将不正确或将找到
更好地匹配关联名称空间中的查找
考虑了所有具有外部链接的函数声明
在所有翻译单元的名称空间中引入,而不仅仅是
考虑到模板定义和
模板实例化上下文,则程序具有未定义的
行为
最后一段指出这是未定义的行为。依赖于模板参数的函数调用这里是f\u helper(typename T::tag\u type{})f\u helper
在实例化f
时不可见,但如果我们在编译完所有翻译单元后执行名称查找,则会可见。我同意,代码格式不正确。我很惊讶g++和clang++都没有关于这一点的警告
14.6.2/1:
以以下形式表达:
- 后缀表达式
(
表达式列表[opt])
如果后缀表达式是id表达式,则如果表达式列表中的任何表达式是类型相关表达式,则id表达式表示相关名称(14.6.2.2)或者,如果id表达式的非限定id是一个模板id,其中任何模板参数都依赖于模板参数……则这些名称未绑定,并在模板定义上下文和实例化点上下文中的模板实例化点(14.6.4.1)处查找
[f_helper
是后缀表达式和id表达式,typename T::tag_type{}
是类型相关的,因此f_helper
是相关名称。]
14.6.4/1:
在解析依赖名称时,将考虑来自以下来源的名称:
- 在模板定义点可见的声明
- 来自与实例化上下文(14.6.4.1)和定义上下文中的函数参数类型相关联的名称空间的声明
14.6.4.1/6:
依赖于模板参数的表达式的实例化上下文是在同一翻译单元中的模板专用化实例化点之前声明的具有外部链接的声明集
14.6.4.2/1:
对于依赖于模板参数的函数调用,使用常用的查找规则(3.4.1、3.4.2、3.4.3)查找候选函数,但以下情况除外:
- 对于使用非限定名称查找(3.4.1)或限定名称查找(3.4.3)的查找部分,只能找到模板定义上下文中的函数声明
- 对于使用关联名称空间(3.4.2)的查找部分,只能找到在模板定义上下文或模板实例化上下文中找到的函数声明
对f_helper(typename T::tag_type{})
的调用取决于模板参数T
,因此在实例化f
之前,名称f helper
不必可见(由于两阶段名称查找)
我相信代码是有效的,因为允许实现将函数模板的实例化点延迟到翻译单元结束,此时可以使用f_helper
的定义
N3936§14.6.4.1/8[温度点]
函数模板、成员函数模板或类模板的成员函数或静态数据成员的专门化可以在翻译单元内具有多个实例化点,并且除了上述实例化点之外,对于在翻译单元内具有实例化点的任何此类专门化翻译单元,翻译单元的末端也被视为实例化点。类模板的专用化在翻译单元内最多有一个实例化点。任何模板的专用化都可能在多个翻译单元中有实例化点。如果两个不同的实例化点对模板进行特殊化,根据一个模板赋予不同的含义
定义规则(3.2),程序格式错误,无需诊断
但是f
不是在f\u助手之前实例化的吗