首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >为什么在ADL中重载更倾向于显式专门化?

为什么在ADL中重载更倾向于显式专门化?
EN

Stack Overflow用户
提问于 2015-01-22 00:16:57
回答 3查看 510关注 0票数 3

考虑一下守则:

代码语言:javascript
运行
复制
#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::交换

有人能解释一下为什么会这样吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-01-22 00:23:36

显式函数模板的专门化从不更改调用函数模板或重载的函数,只有调用模板的实现。

过载解析忽略专门化(相对于重载,对于不熟悉C++函数模板怪癖的人来说,重载看起来很像部分专门化)。

我可以想象为什么:将重载和模板专门化选择规则混合在一起会使规则更难遵循和正确。

一般来说,对函数模板进行专门化很少是个好主意:重载或分派到模板类通常更好。

请注意,语言谈到了过载解决中的“更专门化”:不要把这与“模板专门化”混淆起来:它们是不同的概念,不幸的是它们共享一个单词。

票数 7
EN

Stack Overflow用户

发布于 2015-01-22 00:26:10

函数模板显式专门化不参与过载解析。只考虑从主模板合成的函数声明。如果通过过载解析过程选择其中一个函数作为最佳可行函数,则将在适当的情况下使用相应的主模板的显式专门化。

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

这两个函数都不能基于转换来选择(它们具有相同的函数参数),它们都是模板的专门化,因此考虑了函数模板的偏序,从而使您的swap函数模板更加专业化,因此从您的swap重载合成的函数声明获胜。

票数 8
EN

Stack Overflow用户

发布于 2015-01-22 10:18:08

正如在其他答案中提到的,原因是显式函数专门化的签名没有参与过载解析。

显式专门化的一个简单经验规则是,当使用特定的模板参数调用其主模板时,它们将更改将使用的定义。

代码语言:javascript
运行
复制
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时执行以下步骤(为了简洁起见,我将忽略异常规范):

代码语言:javascript
运行
复制
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),重载解析所使用的候选集将包含以下生成的函数模板专门化:

代码语言:javascript
运行
复制
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赢得过载解析时,将使用显式专门化:

代码语言:javascript
运行
复制
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> &) { }
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28079317

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档