考虑一下守则:
#include <iostream>
#include <algorithm> // std::swap C++98
#include <utility> // std::swap C++11
namespace A
{
template<typename T>
struct Foo {};
template<typename T>
void swap(Foo<T> &lhs, Foo<T> &rhs)
{
    std::cout << "A::swap<T>" << std::endl;
}
} /* end namespace A */
namespace std // we explicitly specialize std::swap here
{
template<> // explicit template specialization for std::swap<int>
void swap(A::Foo<int> &lhs, A::Foo<int> &rhs) 
noexcept 
(is_nothrow_move_constructible<A::Foo<int>>::value && is_nothrow_move_assignable<A::Foo<int>>::value)
{
    std::cout << "std::swap<int>" << std::endl;
} 
} /* end namespace std */
int main()
{
    using std::swap;
    A::Foo<int> a, b; 
    A::Foo<double> x, y;
    swap(a, b); // ADL, expected to call std::swap<Foo<int>>, but NO
    swap(x, y); // ADL, expected to call A::swap<T>, YES
}我希望std::swap显式专门化在调用swap(a, b)中是一个更好的选择,但是似乎总是倾向于重载A::swap,即输出是:
A:交换A::交换
有人能解释一下为什么会这样吗?
发布于 2015-01-22 00:23:36
显式函数模板的专门化从不更改调用函数模板或重载的函数,只有调用模板的实现。
过载解析忽略专门化(相对于重载,对于不熟悉C++函数模板怪癖的人来说,重载看起来很像部分专门化)。
我可以想象为什么:将重载和模板专门化选择规则混合在一起会使规则更难遵循和正确。
一般来说,对函数模板进行专门化很少是个好主意:重载或分派到模板类通常更好。
请注意,语言谈到了过载解决中的“更专门化”:不要把这与“模板专门化”混淆起来:它们是不同的概念,不幸的是它们共享一个单词。
发布于 2015-01-22 00:26:10
函数模板显式专门化不参与过载解析。只考虑从主模板合成的函数声明。如果通过过载解析过程选择其中一个函数作为最佳可行函数,则将在适当的情况下使用相应的主模板的显式专门化。
在这种情况下,重载解析需要在从swap函数模板重载foo<T>&合成的函数声明和从接受T&的std::swap主模板合成的函数声明中进行选择。
这两个函数都不能基于转换来选择(它们具有相同的函数参数),它们都是模板的专门化,因此考虑了函数模板的偏序,从而使您的swap函数模板更加专业化,因此从您的swap重载合成的函数声明获胜。
发布于 2015-01-22 10:18:08
正如在其他答案中提到的,原因是显式函数专门化的签名没有参与过载解析。
显式专门化的一个简单经验规则是,当使用特定的模板参数调用其主模板时,它们将更改将使用的定义。
template <typename T> void foo (T) {
  // definition used for 'foo' when T is not 'int'
}
template <> void foo<int> (int) {
  // definition used for 'foo' when T is 'int'
}编译器在选择最佳swap时执行以下步骤(为了简洁起见,我将忽略异常规范):
namespace A
{
  template <typename T> struct Foo { };
  template <typename T> void swap (Foo<T> &, Foo<T> &);
}
namespace std
{
  // swap provided by the STL
  template <typename T> void swap (T &, T &);
  // explicit specialization of std::swap
  template <> void swap (Foo<int> &, Foo<intT> &) { }
}为两个swaps生成主模板的专门化
13.3.1p7:在每一种情况下,如果候选人是函数模板,则使用模板参数推断(14.8.3,14.8.2)生成候选函数模板专门化。然后以通常的方式将这些候选人作为候选函数处理。
NB:显式专门化并不是其中的一部分。
对于swap(a,b),重载解析所使用的候选集将包含以下生成的函数模板专门化:
A::swap(Foo<int>&, Foo<int>)&);
std::swap(Foo<int>&, Foo<int>)&);这两种都是生成的模板专门化,而且都有完全相同的参数转换序列,因此要确定在最佳可行函数中使用下列模板的模板:
13.3.3p1b7: F1和F2是函数模板的专门化,根据14.5.6.2中描述的偏序规则,F1的函数模板比F2的函数模板更专业化。
偏序可以归结为比较函数参数列表,以找出哪个更专业。在本例中,Foo<T>比T更专门化,因此swap(Foo<T>&, Foo<T>&)被认为比swap(T&,T&)更匹配。结果是选择了A::swap,因此忽略了std::swap的显式专业化。
如果代码被更改,因此显式专门化是针对A::swap而不是std::swap的,那么当A::swap赢得过载解析时,将使用显式专门化:
namespace A
{
  template <typename T> struct Foo { };
  template <typename T> void swap (Foo<T> &, Foo<T> &);
  // explicit specialization of A::swap
  template <> void swap (Foo<int> &, Foo<intT> &) { }
}https://stackoverflow.com/questions/28079317
复制相似问题