提问人:Blair Fonville 提问时间:8/13/2023 最后编辑:Jan SchultkeBlair Fonville 更新时间:8/13/2023 访问量:87
为什么函数模板专用化是不同的,尽管返回类型或参数没有区别?
Why are function template specializations distinct, despite no difference in return type or parameters?
问:
为什么以下代码有效?
#include <iostream>
#include <cstdint>
template <class D>
void whichD() {
std::cout << "D is " << sizeof(D) << " byte(s)\n";
}
int main(int argc, char **argv)
{
if (argv[1][0] == '1') {
whichD<uint8_t>();
}
else {
whichD<uint16_t>();
}
}
程序输出./temp 1 && ./temp 2
D is 1 byte(s)
D is 2 byte(s)
我没想到它会起作用,原因与你不能拥有的原因相同:
void foo() { /* ... */ }
void foo() { /* ... */ } // error: re-definition
int bar() { /* ... */ }
double bar() { /* ... */ } // error: re-definition
答:
编译器根据模板参数进行区分。
在 的示例中,如果您调用 ,编译器无法推断出调用了哪个函数重载,因此会引发错误。foo
foo()
这里,是相同的函数,但模板参数不同。在编译过程中的某个时候,这些函数调用将被转换为符号(例如 和分别,或类似的东西)。 和是两个不同的函数名称,所以没有歧义。whichD<uint8_t>
whichD<uint16_t>
whichD_uint8_t
whichD_uint16_t
whichD_uint8_t
whichD_uint16_t
的确,函数必须具有不同的签名。 否则,它们是彼此的重新定义。 但是,签名的构成取决于实体的种类。
非模板函数的签名
对于非模板函数,签名定义为:
签名
⟨function⟩名称、parameter-type-list 和封闭命名空间
例如:
void foo()
并具有不同的签名,因为它们具有不同的名称void bar()
void foo(int)
并具有不同的参数类型列表void foo(float)
void a::foo()
并具有不同的封闭命名空间void b::foo()
void foo()
并且是彼此的重新定义;返回类型不是签名的一部分,因此这些函数不是不同的。int foo()
函数模板专用化的签名
具有给定模板参数(例如)的函数模板(例如)称为函数模板专用化。
函数模板专用化的特征是:whichD
whichD<uint8_t>
签名
⟨函数模板专用化⟩它所特化的模板的签名及其模板参数(无论是显式指定还是推导)
- [defns.signature.templ.spec]
在您的示例中,是同一函数模板的专用化,但它们的模板参数不同,使它们成为不同的函数。whichD<uint8_t>
whichD<uint16_t>
编译器不只是放弃所有的模板性并使用模板来生成:
void whichD() { /* ... */ }
void whichD() { /* ... */ } // error: re-definition
相反,编译器将实例化这些专用化、包含的参数,并生成如下内容:
void whichD<uint8_t>() { /* ... */ }
void whichD<uint16_t>() { /* ... */ } // OK, different from above
这两个专业化的签名会被破坏,因此(以某种破坏的形式)包含在链接器看到的符号中。uint8_t
uint16_t
这些规则背后的原因
请记住,这些规则的存在是为了区分可以单独调用函数的情况,以及不能单独调用函数的情况。如果 和 were 不是专用化,而是带有名称的常规函数,您将无法调用两者之一。
过载分辨率无法区分它们。whichD<uint8_t>
whichD<uint16_>
whichD
但是,正如您在代码中演示的那样,通过显式提供模板参数,可以很容易地单独调用和调用。
如果说这两者具有相同的签名,那将是一种武断的限制。whichD<uint8_t>
whichD<uint16_t>
评论
foo
whichD
whichD<uint8_t>
和是两种不同的(且完全指定的)类型。或者最好说,两个不同的功能有两个不同的名称。提供模板参数后,您将获得一个具体类型。whichD<uint16_t>
foo
whichD
whichD<int8>
whichD<int16>
whichD_int8
whichD_int16