重载模板函数时显式专用化和常规函数之间的区别

Difference between explicit specialization and regular functions when overloading a template function

本文关键字:函数 常规 之间 区别 重载 专用      更新时间:2023-10-16

我今天在滚动。这是n00b问题7:

当您尝试重载模板函数时,显式专用化和仅常规函数之间有什么区别?

使用显式专业化的适当情况是什么?我不太明白:

#include <iostream>
template <typename s> void test(s var1);
template <> void test<int>(int var1);
int main(){
    test(1);
    test(1.1);
    test("hello!!");
    return 0;
}
template <typename s> void test(s var1){
    std::cout << var1 << std::endl;
}
template <> void test<int>(int var1){
    std::cout << "int " << var1 << std::endl;
}

反对:

#include <iostream>
template <typename s> void test(s var1);
void test(int var1);
int main(){
    test(1);
    test(1.1);
    test("hello!!");
    return 0;
}
template <typename s> void test(s var1){
    std::cout << var1 << std::endl;
}
void test(int var1){
    std::cout << "int " << var1 << std::endl;
}

显式专用模板函数和非模板常规函数之间实际上没有区别,除了当编译器为函数调用查找匹配的签名类型时,它将首先选择一个与所需签名匹配的非模板函数,然后再尝试实例化任何可能满足所需签名匹配的可用模板函数。

如果要在不是模板函数的头文件中声明和定义一个函数,则必须将该函数声明为 inline 。 这是因为模板函数在实际实例化之前不是与代码模块链接的实际函数。然后,链接器在编译代码模块后丢弃该实例化。 如果链接器没有这样做,则每次.cpp文件包含头文件时,链接器都会抱怨函数的定义重复。 在非模板函数上使用 inline 关键字在编译器级别具有类似的效果,因为每当在 .cpp 文件中使用该函数时,编译器都会将该函数调用替换为头文件中inline函数的函数代码主体,并避免函数调用与关联的堆栈活动记录设置和清理的开销。 因此,链接器不会抱怨函数的重复定义。

我不是专家,但我的经验是在我想定义不同的返回类型时使用模板(和专业化)。 不能重载函数的返回类型。

一个主要的区别是: 显式专用化根本不参与重载。

template<typename T> void f(T const&);
template<> void f<char const*>(char const * const&);

f("hello")通话不会考虑任何明确的专业化。它只会采用所有模板,并推断出它们的模板参数。以上T将被推导出为char[6],因此不会选择专业。

如果改为重载函数模板,则具有完全不同的特征。

template<typename T> void f(T const&);
void f(char const * const&);

调用这个函数,它将选择第二个函数,因为生成的专用化的(char const(&)[6])参数和(char const * const&)参数都同样匹配参数,但第二个函数是非模板函数,因此最终首选它。

当编译器遇到函数调用时,它首先查找非模板函数定义,然后查找显式专用模板,最后查找签名与函数调用匹配的模板定义。因此,例如,如果您有一个显式定义的模板和一个模板,则编译器会选择显式定义的模板。因此,当您希望以与模板处理特定数据类型的方式不同的方式处理特定数据类型时,应使用显式专用模板。

显式专用化的另一个用途是当您想要"重载"不接受任何参数的函数时。您可以使用显式专用化为函数提供不同的定义,以处理不同的数据类型。

我直言,当您要使用显式模板参数调用该函数时,应该使用function template的显式专用化。

例如
test<int>(myClass); // no argument for 'int' -> declare test() as template specialization

在其他情况下,您应该始终使用正常的专业化。

test(10, myClass); // argument 10 is 'int' -> test() should be normal specialized

从技术上讲,函数的正常模板专用化和显式模板专用化之间没有区别。普通的专用版本完全独立于template功能。