c++ - 函数模板签名

标签 c++ templates language-lawyer

是什么决定了两个函数模板声明是声明了同一个模板,还是同名的重载?

答案开头在3.5p9:

Two names that are the same (Clause 3) and that are declared in different scopes shall denote the same variable, function, type, enumerator, template or namespace if

  • both names have external linkage or else both names have internal linkage and are declared in the same translation unit; and

  • both names refer to members of the same namespace or to members, not by inheritance, of the same class; and

  • when both names denote functions, the parameter-type-lists of the functions (8.3.5) are identical; and

  • when both names denote function templates, the signatures (14.5.6.1) are the same.

非模板非成员函数的签名是(1.3.17):

signature

<function> name, parameter type list (8.3.5), and enclosing namespace (if any)

[Note: Signatures are used as a basis for name mangling and linking. -- end note]

提到两次的parameter-type-list 已经在第 8.3.5p5 节中定义。该段描述了函数参数的实际类型如何根据声明的类型进行调整,用指针替换数组和函数,并丢弃顶级 cv-qualifiers。然后,

The resulting list of transformed parameter types and the presence or absence of the ellipsis or a function parameter pack is the function's parameter-type-list.

因此,在非模板情况下,parameter-type-list 显然是类型的概念语义列表(可能加上花哨的结尾),而不是标记序列或句法结构。正如我们所料,以下内容违反了 ODR,因为这两个定义定义了相同的函数:

void f(int, int*) {}
void f(int p, decltype(p)*) {}

在模板情况下,我们有 (1.3.18):

signature

<function template> name, parameter type list (8.3.5), enclosing namespace (if any), return type, and template parameter list

现在考虑:

template<typename T> void g(int, int*, T, T*) {}               // #1
// template<typename T> void g(int p, decltype(p)*, T, T*) {}  // #2
template<typename T> void g(int, int*, T q, decltype(q)*) {}   // #3

g++ -std=c++0x 版本 4.6.3 提示定义 #1 和 #2 定义了相同的函数,但接受 #1 和 #3 作为重载没有问题。 (它还认为 #3 比 #1 更专业,没有办法调用 #1,但这是一个切线问题。)#2 和 #3 之间的主要区别是 q 是类型-dependent 而 p 不是。所以我猜 decltype(q) 的含义在模板实例化之前无法确定?标准保证这种行为吗?

对于函数模板,必须允许parameter-type-list 的含义包括尚未被实例化替换的模板参数,以及相关名称和所有其他内容。但这使得了解两个声明是否等同变得棘手(如果可能的话)。

14.5.6.1 的第 5-6 段解决了类似的问题,它定义了等价表达式和等价函数模板声明(相同的标记序列,除了不同的声明可能对模板参数使用不同的标识符),功能等效表达式和功能等效函数模板声明(实例化后相同),要求:

If a program contains declarations of function templates that are functionally equivalent but not equivalent, the program is ill-formed; no diagnostic is required.

第 5 段中的示例演示了安全等效的函数模板:

template <int I, int J> void f(A<I+J>);  // #1
template <int K, int L> void f(A<K+L>);  // same as #1

第 7 段中的一个示例表明违反了该规则:

// Ill-formed, no diagnostic required
template <int I> void f(A<I>, A<I+10>);
template <int I> void f(A<I>, A<I+1+2+3+4>);

但这不适用于上面的示例 g 函数。 T*decltype(q)* 在类型等价的一些类似定义下可能被认为是功能等价的,但第 14.5.6.1 节只说明了表达式的替换,而不是类型.

最佳答案

标准有一个安静的未明确定义的类型等价规则,主要基于限定名称的语法,但对于模板参数,是参数列表中的位置和该列表的嵌套深度(即它是否是其中之一成员模板或封闭类模板的成员模板)。

typedef 会记住特定的依赖类型。然而,14.4p2 的 decltype 类型是一个不同的类型,不等同于 T。

关于c++ - 函数模板签名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14420908/

相关文章:

c++ - 不正确的类型转换 - 类型转换或未定义行为的使用

c++ - 嵌套的重载运算符?

c++ - 在 C++ 中更改结构内部元素的方法?

c++ - 在 C++ 中提取另一个泛型类型的泛型类型

javascript - Nunjacks 图标计数宏

c++ - 生成模板定义的宏

c++ - 复制省略直接基类初始化?

c++ - 为什么 init-capturing 可变 lambda 不能有可变数据成员?

c++:Istream 将 .txt 文件中的每个换行符计为两个

c++ - 如何阻塞线程并恢复它