在Josuttis和Vandevoorde着名的模板书C++ Templates: The Complete Guide中,他们讨论了有关函数模板重载的细节。
在他们的一个示例中,与函数签名和重载函数模板的讨论相关,它们呈现了他们用以下术语描述的代码:
This program is valid and produces the following output:
(Note: Output shown below)
但是,当我在Visual Studio 2010中构建和编译相同的代码时,我得到了不同的结果。这让我相信VS 2010编译器产生的代码不正确,或者Josuttis代码有效是不正确的。
这是代码。 (Josuttis 2003,Section 12.2.1)
// File1.cpp
#include <iostream>
template<typename T1, typename T2>
void f1(T2, T1)
{
std::cout << "f1(T2, T1)" << std::endl;
}
extern void g();
int main()
{
f1<char, char>('a', 'b');
g();
}
...
// File2.cpp
#include <iostream>
template<typename T1, typename T2>
void f1(T1, T2)
{
std::cout << "f1(T1, T2)" << std::endl;
}
void g()
{
f1<char, char>('a', 'b');
}
(注意两个模板函数定义中类型参数的反转。另请注意,当两个类型参数相同时,此反转不起作用,因为它们适用于此代码示例中的两个函数f1()
。)
Josuttis说:
This program is valid and produces the following output:
f1(T2, T1)
f1(T1, T2)
当我在Visual Studio 2010编译器中构建并运行相同的代码(未更改)时,这是我的结果:
f1(T1, T2)
f1(T1, T2)
此外,我想知道编译器/链接器如何区分在file1.cpp中实例化的函数f1
和在file2.cpp中实例化的函数f1
,给定(我认为)编译器剥离了这些函数是从模板创建的事实的所有“知识”,并且只有函数签名本身的信息(我认为):void (char, char)
,这是相同的两个f1
函数。
由于(如果我是正确的)两个翻译单元中的函数签名是相同的,我认为这是违反One Definition Rule(ODR)的一个例子,因此它将是无效的C ++。
然而,正如我刚才所说,Josuttis和Vandevoorde声称这是有效的 C ++。
但是由于我的相同代码的编译版本提供不同的结果而不是Josuttis声称的输出,这似乎表明VS 2010产生的代码不正确,或者Josuttis在此不正确case(即代码无效并违反ODR)。
Josuttis和Vandevoorde是不正确的,还是VS 2010产生了不正确的输出?或者是否有其他解释可以解释VS 2010产生的输出与Josuttis报告的输出之间的差异?
在调用每个f1()
时显示VS 2010反汇编可能会很有意义。
f1()
的第一个电话(直接在main()
内):
f1()
的第二次调用(来自g()
内):
请注意,编译器在两种情况下选择的f1()
的地址都是相同的 - 13E11EAh。对我来说,这表明实际上,编译器无法区分两个实例化的函数签名,这是ODR被违反的情况,因此代码无效C ++ 并且Josuttis有错误在他的书中。但它只是 - 一个迹象。我不知道。
(我已经检查过本书网站上的勘误表,但没有提及这个例子。)
ADDENDUM 根据评论的请求,我附加了此程序的.map文件的相关输出,其中显示了用于f1
的错位名称:
ADDENDUM 2 现在问题得到解答 - Josuttis的书是正确的 - 我想在Josuttis的文本中,在同一部分(12.2.1)中,明确地概述了什么决定了一个独特的功能签名,包括模板方面。
从文本(其中包括定义函数签名的预期事物)中,TRANSLATION UNIT是函数签名的一部分;对于模板函数(仅),RETURN TYPE是函数签名的一部分,
0.6。模板参数和模板参数,如果函数是从函数模板生成的。
因此 - 很清楚。即使在实例化函数模板之后,编译器也必须维护和跟踪模板信息,以便编译器/链接器遵守模板的必要特殊规则(如我的问题中的代码示例)。 / p>
答案 0 :(得分:8)
为早先的错误答案道歉。这个例子看起来确实是正确的,并且标准本身实际上有一个类似的例子(C ++ 11,14.5.6.1 / 1-2)。我只想完整地引用它:
可以重载函数模板,以便两个不同的函数模板特化具有相同的类型。 [示例:
// file1.c template<class T> void f(T*); void g(int* p) { f(p); // calls f<int>(int*) } // file2.c template<class T> void f(T); void h(int* p) { f(p); // calls f<int*>(int*) }
- 结束示例]
- 此类专业化是不同的功能,不违反一个定义规则(3.2)。
醇>
在您的情况下,您有两个不同的函数模板,都称为f1
(这很好,因为您可以重载函数模板),并且它们碰巧具有相同类型的特化。