为什么重载优先于ADL中的显式特化

vso*_*tco 3 c++ templates overloading argument-dependent-lookup c++11

考虑一下代码:

#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
}
Run Code Online (Sandbox Code Playgroud)

我希望std::swap显式特化是调用中更好的候选者swap(a, b),但似乎总是首选重载 A::swap,即输出是:

A::swap<T>
A::swap<T>
Run Code Online (Sandbox Code Playgroud)

任何人都可以解释为什么会这样

bog*_*dan 8

函数模板显式特化不参与重载决策.仅考虑从主模板合成的函数声明.如果通过重载解析过程选择一个这样的函数作为最佳可行函数,则如果合适,将使用对应主模板的显式特化.

在这种情况下,重载解析需要在从swap函数模板重载合成的函数声明foo<T>&和从std::swap主模板合成的函数声明之间进行选择T&.

基于转换(它们具有相同的函数参数),这两个函数都不能相互选择(它们具有相同的函数参数),两者都是模板特化,因此考虑了函数模板的部分排序,这使得swap函数模板更加专业化,因此函数声明从你的swap超负荷合成.


Yak*_*ont 7

显式函数模板特化永远不会改变调用哪个函数模板或重载,只调用模板的实现.

重载决策忽略了特化(与重载相反,对于不熟悉C++函数模板怪癖的人来说,这看起来很像部分特化).

我可以想象为什么:混合重载和模板特化选择规则会使规则更难以遵循和正确.

通常,专门化函数模板很少是一个好主意:重载或调度到模板类通常更好.

请注意,该语言在重载决策中讨论"更专业化":不要将此与"模板专业化"混淆:它们是不同的概念,不幸地共享一个单词.