模板特殊化和重载的功能之间的差异?(Differences between template spe

2019-07-20 15:50发布

所以,我知道有代码这两花絮之间的差异:

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

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

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

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

我很困惑,这两位之间的功能差异。 可有人告诉某些情况下,这些snippits相互采取不同?

Answer 1:

I can only think of a few differences - here are some examples that don't necessarily cause harm (i think). I'm omitting definitions to keep it terse

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

That is because specializations are not found by name lookup, but by argument matching, so a using declaration will automatically consider a later introduced specialization.

Then, you of course cannot partially specialize function templates. Overloading however accomplishes something very similar by partial ordering (using different types now, to make my point)

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
}

That wouldn't be possible with function template explicit specialization. Another example is when references are involved, which causes template argument deduction to look for an exact match of the types involved (modulo base/derived class relationships and constness):

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);
}

I recommend you to always use overloading, because it's richer (allows something like partial specialization would allow), and in addition you can place function in whatever namespace you want (although then it's not strictly overloading anymore). For example, instead of having to specialize std::swap in the std:: namespace, you can place your swap overload in your own namespace and make it callable by ADL.

Whatever you do, never mix specialization and overloading, it will be a hell of a mess like this article points out. The Standard has a lovely paragraph about it

The placement of explicit specialization declarations for function templates, class templates, member functions of class templates, static data members of class templates, member classes of class templates, member class templates of class templates, member function templates of class templates, member functions of member templates of class templates, member functions of member templates of non-template classes, member function templates of member classes of class templates, etc., and the placement of partial specialization declarations of class templates, member class templates of non-template classes, member class templates of class templates, etc., can affect whether a program is well-formed according to the relative positioning of the explicit specialization declarations and their points of instantiation in the translation unit as specified above and below. When writing a specialization, be careful about its location; or to make it compile will be such a trial as to kindle its self-immolation.



Answer 2:

模板专业化不仅仅是超载更通用。 您可以专注之类的东西类,而不是只是简单的功能。 只有重载适用于功能。

UPDATE:澄清每阿拉克的评论越多,你真的在这里比较苹果和橘子。 函数重载是用来介绍具有不同的功能共享一个名字,如果他们有不同的签名的能力。 模板特用于定义用于特定类型参数的特定代码段。 你不能有一个模板专业化,如果你没有一个模板。 如果您删除了第一段代码声明了通用的模板,因此当你尝试使用模板特收到编译时错误。

因此,模板专业化的目标是从函数重载相当不同。 他们只是碰巧同样表现在你的榜样,而他们是完全不同的。

如果你提供过载,您声明,恰好有一个同名的方法。 你不会阻止与特定类型的参数一起使用的模板。 为了证明这一点,请尝试:

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.
}

正如可以看到,在这个例子中,有两种不同的inc为函数int值: inc(const int&)inc<int>(const int&) 你不能扩大使用通用模板int如果你曾经使用模板特殊化。



Answer 3:

一个这样的例子:

#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);
}

它实际上是建议您使用的功能非模板重载,并留下专业化的课程。 它是在更大的长度在讨论为什么并不是专门函数模板?



Answer 4:

据我所知,没有功能上的差异。 我可以补充的是,如果你有两个模板功能的专业化和定义一个普通函数,那么有没有超载歧义的普通函数是有利的。



Answer 5:

只是为了阐述对litb在他提到的第一点答案 。 一旦重载实际上已经选择了主模板专业化只检查。 其结果可能会导致一些惊喜,其中一个功能超载且有明确的专业:

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);
}

当选择调用哪个函数,将发生以下步骤:

  1. 名称查找发现小学模板。
  2. 每个模板是专门和重载决议试图基于参数与参数之间的转换选择最佳的功能。
  3. 在thise情况下,在转换的质量没有区别。
  4. 然后部分排序规则来选择最专业的模板。 在这种情况下是第二parimary为“foo(T *)”。

只有经过这些步骤,当最好的功能已选择将所选功能的明确特予以考虑。 (在这种情况下初级#2现在没有所以没有被认为是)。

在这里调用上面明确专业化的唯一方法,就是在调用实际使用显式模板参数:

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

一个好的经验法则是尽量避免具有同样explicily专门的过载,所得到的规则是非常复杂的。



文章来源: Differences between template specialization and overloading for functions?