L. *_* F. 5 c++ templates instantiation language-lawyer name-lookup
当从非模板上下文引用函数模板时,该标准允许在封闭的命名空间范围声明之后或在翻译单元的末尾实例化函数模板:[temp.point]/1
对于函数模板特化、成员函数模板特化或类模板的成员函数或静态数据成员的特化,如果特化是隐式实例化的,因为它是从另一个模板特化和它所在的上下文中引用的引用取决于模板参数,特化的实例化点是封闭特化的实例化点。 否则,这种特化的实例化点紧跟在引用该特化的命名空间范围声明或定义之后。
函数模板、成员函数模板或类模板的成员函数或静态数据成员的特化可能在翻译单元内具有多个实例化点,并且除了上述实例化点之外,对于任何此类在翻译单元内有一个实例化点的特化,翻译单元的结尾也被认为是一个实例化点。类模板的特化在翻译单元内最多有一个实例化点。任何模板的特化都可能在多个翻译单元中具有实例化点。如果根据一个定义规则,两个不同的实例化点赋予模板特化不同的含义,则程序格式错误,无需诊断。
现在考虑这个最小的可重现示例:
#include <iostream>
#include <array>
struct A {};
std::array<char, 2> show(float, A)
{
std::cout << "2\n";
return {};
}
template<typename T>
struct Fun {
decltype(show(0, T{})) b;
};
template <typename T>
void func(T, int c = sizeof(Fun<T>{}.b))
{
show(0, T{});
std::cout << c << '\n';
}
int main()
{
func(A{});
}
char show(int, A)
{
std::cout << "1\n";
return {};
}
Run Code Online (Sandbox Code Playgroud)
GCC 和 Clang 输出1 2(Godbolt)。
在这里,func<A>(triggered in main) 的实例化有两个实例化点:一个紧接在main(因此在第二个 之前show),另一个在翻译单元的末尾。第一个1指示编译器func<A>在翻译单元的末尾进行实例化。但是,默认参数sizeof(Fun<T>{}.b)导致Fun<A>被实例化,第二个2建议Fun<A>在第二个之前实例化show。
现在,默认参数的实例化点被指定为func<A>:[temp.point]/2
如果以使用该函数模板或成员函数的默认参数定义的方式调用类模板的函数模板或成员函数,则默认参数的实例化点是函数模板的实例化点或成员函数专业化。
嗯……这好像暗示这两个数字应该是一样的。
我觉得我在这里错过了一些东西。有没有什么细节我碰巧忽略了?还是我犯了错误?
正如问题[temp.point]/8中引用的那样:
如果根据单一定义规则,两个不同的实例化点赋予模板专门化不同的含义,则该程序是格式错误的,无需诊断。
根据单一定义规则,如果定义中使用的名称的函数调用重载解析将产生定义外定义的不同实体,则两个定义不相同。( [basic.def.odr]/6.2 )
show对infunc<A>和 in的两次调用的重载解析Fun<A>将根据 的func<A>实例化点是紧接在翻译单元之后main还是在翻译单元的末尾来选择不同的函数重载,这两个点都是允许的实例化点。
因此该程序格式错误,无需诊断。
| 归档时间: |
|
| 查看次数: |
159 次 |
| 最近记录: |