C++ 函数的模板专门化和重载之间的区别?

C++ 函数的模板专门化和重载之间的区别?,c++,templates,overloading,C++,Templates,Overloading,因此,我知道这两种代码之间存在差异: template <typename T> T inc(const T& t) { return t + 1; } template <> int inc(const int& t) { return t + 1; } 模板 T公司(const T&T) { 返回t+1; } 模板 国际公司(const国际和技术) { 返回t+1; } 及 模板 T公司(const T&T) { 返回t+1; }

因此,我知道这两种代码之间存在差异:

template <typename T>
T inc(const T& t)
{
    return t + 1;
}

template <>
int inc(const int& t)
{
    return t + 1;
}
模板
T公司(const T&T)
{
返回t+1;
}
模板
国际公司(const国际和技术)
{
返回t+1;
}

模板
T公司(const T&T)
{
返回t+1;
}
国际公司(const国际和技术)
{
返回t+1;
}

我不清楚这两者之间的功能区别是什么。有人能展示一些情况下这些狙击手的行为彼此不同吗?

模板专门化比重载更通用。您可以专门化类之类的东西,而不仅仅是简单的函数。重载仅适用于函数

更新:为了澄清阿拉克的评论,你在这里真的比较了苹果和桔子。函数重载用于引入使不同的函数共享一个名称的能力,如果它们具有不同的签名。模板专门化用于为特定类型参数定义特定代码段。如果没有模板,则无法进行模板专门化。如果删除声明泛型模板的第一段代码,如果尝试使用模板专门化,将收到编译时错误

因此,模板专门化的目标与函数重载截然不同。他们只是碰巧在你的例子中表现得相似,而他们却根本不同

如果提供重载,则声明的是一个恰好具有相同名称的独立方法。您没有阻止模板与特定类型参数一起使用。要证明这一事实,请尝试:

template <typename T>
T inc(const T& t)
{
    return t + 1;
}

int inc(const int& t)
{
    return t + 42;
}

#include <iostream>
int main() {
   int x = 0;
   x = inc<int>(x);
   std::cout << "Template: " << x << std::endl; // prints 1.

   x = 0;
   x = inc(x);
   std::cout << "Overload: " << x << std::endl; // prints 42.
}
模板
T公司(const T&T)
{
返回t+1;
}
国际公司(const国际和技术)
{
返回t+42;
}
#包括
int main(){
int x=0;
x=inc(x);

std::coutAFAIK没有功能差异。我能补充的是,如果同时定义了模板函数专门化和普通函数,那么就没有重载歧义,因为普通函数是受欢迎的。

一个这样的例子:

#include <cstdio>

template <class T>
void foo(T )
{
    puts("T");
}

//template <>
void foo(int*)
{
    puts("int*");
}

template <class T>
void foo(T*)
{
    puts("T*");
}

int main()
{
    int* a;
    foo(a);
}
#包括
模板
空富(T)
{
看跌期权(“T”);
}
//模板
void foo(int*)
{
看跌期权(“int*”);
}
模板
void foo(T*)
{
看跌期权(“T*”);
}
int main()
{
int*a;
傅(甲),;
}

实际上,有人建议对函数使用非模板重载,而对类保留专门化。在

中对其进行了更详细的讨论。我只能想到一些不同之处-这里有一些不一定会造成伤害的示例(我认为)。为了保持简洁,我省略了定义

template <typename T> T inc(const T& t);
namespace G { using ::inc; }
template <> int inc(const int& t);
namespace G { void f() { G::inc(10); } } // uses explicit specialization

// --- against ---

template <typename T> T inc(const T& t);
namespace G { using ::inc; }
int inc(const int& t);
namespace G { void f() { G::inc(10); } } // uses template
template T inc(const T&T);
命名空间G{using::inc;}
模板集成公司(const集成与测试);
命名空间G{void f(){G::inc(10);}}//使用显式专门化
//---反对---
模板T公司(const T&T);
命名空间G{using::inc;}
国际公司(const int&t);
命名空间G{void f(){G::inc(10);}}//使用模板

这是因为专业化不是通过名称查找而是通过参数匹配找到的,所以使用声明将自动考虑稍后引入的专业化。

当然,您不能部分地专门化函数模板。但是重载通过偏序(现在使用不同的类型来说明我的观点)完成了非常类似的事情

template void f(T);//为非指针调用
模板void f(T*T);//为指针调用。
INTA;
void e(){
f(a);//调用非指针版本
f(&a);//调用指针版本
}
函数模板显式专门化不可能做到这一点。另一个例子是当涉及引用时,这会导致模板参数推断查找所涉及类型的精确匹配(模基/派生类关系和常量):

模板空f(T常数&);
模板空f(int*const&);
模板无效g(T常数&);
空g(整数*常数&);
int a[5];
void e(){
//调用主模板,而不是显式专门化
//因为'T'是'int[5]`,而不是'int*`
f(a);
//调用函数而不是模板,因为函数是
//精确匹配(指针转换成本不够高),而且
//首选。
g(a);
}

我建议您总是使用重载,因为它更丰富(允许部分专门化),此外,您可以将函数放置在任何您想要的名称空间中(尽管这样就不再严格重载)例如,您不必在
std::
命名空间中专门化
std::swap
,而可以将
swap
重载放置在自己的命名空间中,并使其可由ADL调用

无论你做什么,千万不要把专业化和重载混为一谈,这将是一个地狱般的混乱,就像你所指出的那样

函数模板、类模板、类模板的成员函数、类模板的静态数据成员、类模板的成员类、类模板的成员类模板、类模板的成员函数模板、类模板的成员函数模板的显式专门化声明的放置最近,非模板类的成员模板的成员函数、类模板的成员类的成员函数模板等,以及类模板的部分专门化声明、非模板类的成员类模板、类模板的成员类模板等的放置,都会影响程序是否运行良好-根据明确的专门化声明的相对位置及其在翻译单元中的实例化点(如上和下所述)形成。编写专门化时,请注意其位置;否则,使其编译将是一种尝试,从而点燃其自焚


我只是想详细说明一下litb在hi中提到的第一点
template <typename T> T inc(const T& t);
namespace G { using ::inc; }
template <> int inc(const int& t);
namespace G { void f() { G::inc(10); } } // uses explicit specialization

// --- against ---

template <typename T> T inc(const T& t);
namespace G { using ::inc; }
int inc(const int& t);
namespace G { void f() { G::inc(10); } } // uses template
template <typename T> void f(T t); // called for non-pointers
template <typename T> void f(T *t); // called for pointers.

int a;
void e() {
  f(a); // calls the non-pointer version
  f(&a); // calls the pointer version
}
template<typename T> void f(T const &);
template<> void f(int * const &);

template<typename T> void g(T const &);
void g(int * const &);

int a[5];
void e() {
  // calls the primary template, not the explicit specialization
  // because `T` is `int[5]`, not `int *`
  f(a);

  // calls the function, not the template, because the function is an
  // exact match too (pointer conversion isn't costly enough), and it's 
  // preferred. 
  g(a);
}
template <typename T> void foo (T);  // Primary #1
template <> void foo<int*> (int*);   // Specialization of #1

template <typename T> void foo (T*); // Primary #2

void bar (int * i)
{
  foo(i);
}
void bar (int * i)
{
  foo<int*> (i);  // expliit template argument forces use of primary #1
}