为什么两阶段查找无法选择“swap”的重载版本?

2023-11-26

我在学习这个令人着迷的答案 to a 微妙的问题关于实施的最佳实践swap用户定义类型的函数。 (我的问题最初是由讨论向命名空间添加类型的非法性std.)

我不会在此处重新打印上述链接答案中的代码片段。

相反,我想理解答案。

我在上面链接的答案在第一个代码片段下方指出了超载swap in namespace std(而不是专门化它在该命名空间中):

如果你的编译器打印出不同的东西,那么它就不是 正确实现模板的“两阶段查找”。

答案接着指出专门化swap in namespace std(相对于超载 it) 产生不同的结果(在以下情况下期望的结果专业化).

然而,答案还涉及一个额外的案例:专门用于用户定义的交换template class- 在这种情况下,同样无法达到预期的结果。

不幸的是,答案很简单states事实;它没有解释why.

有人可以详细说明该答案,并描述该答案中提供的两个特定代码片段中的查找过程:

  • 超载swap in namespace std对于用户定义的非模板类(如链接答案的第一个代码片段所示)

  • 专门化swap in namespace std对于用户定义的模板类(如链接答案的最终代码片段所示)

在这两种情况下,通用std::swap被调用,而不是用户定义的swap. Why?

(这将阐明两阶段查找的本质,以及实现用户定义的最佳实践swap;谢谢。)


序言包含大量标准语言

致电给swap()在示例中需要一个从属名称,因为它的参数begin[0] and begin[1]取决于模板参数T周围的algorithm()函数模板。标准中定义了此类从属名称的两阶段名称查找,如下所示:

14.6.4.2 候选函数 [temp.dep.candidate]

1 对于后缀表达式是从属名称的函数调用, 使用通常的查找规则(3.4.1, 3.4.2) 除了:

— 对于使用非限定名称查找的部分(3.4.1),仅来自模板定义的函数声明 找到上下文。

— 对于使用关联的查找部分 命名空间 (3.4.2),仅在以下任意一个中找到的函数声明 模板定义上下文或模板实例化上下文是 成立。

不合格的查找定义为

3.4.1 非限定名称查找 [basic.lookup.unqual]

1 在 3.4.1 列出的所有情况下,都会在范围中搜索 按照每个类别中列出的顺序进行声明;一旦找到声明,名称查找就会结束为了名字。如果不 发现声明,程序格式错误。

和参数相关的查找(ADL)为

3.4.2 参数相关名称查找 [basic.lookup.argdep]

1 当函数调用(5.2.2)中的后缀表达式为一个 不合格的 ID,通常情况下未考虑的其他名称空间 可以搜索不合格的查找(3.4.1),并且在这些命名空间中, 命名空间范围的友元函数或函数模板声明 (11.3) 可能会发现不可见的情况。这些修改对 搜索取决于参数的类型(对于模板模板 参数,模板参数的命名空间)。

将标准应用于示例

The 第一个例子 calls exp::swap()。这不是从属名称,不需要两阶段名称查找。因为对 swap 的调用是限定的,所以会进行普通查找,仅查找通用的swap(T&, T&)函数模板。

The 第二个例子(@HowardHinnant 称之为“现代解决方案”)调用swap()并且也有过载swap(A&, A&)在与 where 相同的命名空间中class A生存(在本例中为全局命名空间)。因为对 swap 的调用是无限定的,所以普通查找和 ADL 都发生在定义点(同样只查找泛型)swap(T&, T&))但另一个 ADL 发生在实例化点(即exp::algorithm()正在被召唤main())这就会出现swap(A&, A&)这是在重载解析期间更好的匹配。

到目前为止,一切都很好。现在再演一遍:第三个例子 calls swap()并且有一个专业template<> swap(A&, A&) inside namespace exp。查找与第二个示例中的相同,但现在 ADL 不选择模板专业化,因为它不在关联的命名空间中class A。然而,尽管专业template<> swap(A&, A&)在重载决策期间不起作用,它仍然在使用时实例化。

最后,第四个例子 calls swap()并且有过载template<class T> swap(A<T>&, A<T>&) inside namespace exp for template<class T> class A生活在全局命名空间中。查找与第三个示例中的相同,并且 ADL 再次不会拾取过载swap(A<T>&, A<T>&)因为它不在类模板的关联命名空间中A<T>。在这种情况下,也没有必须在使用时实例化的专门化,因此通用swap(T&, T&)正在这里被调用。

结论

即使您不允许添加新的重载namespace std,并且只有显式专业化,它甚至无法工作,因为两阶段名称查找的各种复杂性。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么两阶段查找无法选择“swap”的重载版本? 的相关文章

  • 我如何才能等待多个事情

    我正在使用 C 11 和 stl 线程编写一个线程安全队列 WaitAndPop 方法当前如下所示 我希望能够将一些内容传递给 WaitAndPop 来指示调用线程是否已被要求停止 如果 WaitAndPop 等待并返回队列的元素 则应返回
  • WCF RIA 服务 - 加载多个实体

    我正在寻找一种模式来解决以下问题 我认为这很常见 我正在使用 WCF RIA 服务在初始加载时将多个实体返回给客户端 我希望两个实体异步加载 以免锁定 UI 并且我想利用 RIA 服务来执行此操作 我的解决方案如下 似乎有效 这种方法会遇到
  • GLKit的GLKMatrix“列专业”如何?

    前提A 当谈论线性存储器中的 列主 矩阵时 列被一个接一个地指定 使得存储器中的前 4 个条目对应于矩阵中的第一列 另一方面 行主 矩阵被理解为依次指定行 以便内存中的前 4 个条目指定矩阵的第一行 A GLKMatrix4看起来像这样 u
  • Web 客户端和 Expect100Continue

    使用 WebClient C NET 时设置 Expect100Continue 的最佳方法是什么 我有下面的代码 我仍然在标题中看到 100 continue 愚蠢的 apache 仍然抱怨 505 错误 string url http
  • 为什么两个不同的 Base64 字符串的转换会返回相等的字节数组?

    我想知道为什么从 base64 字符串转换会为不同的字符串返回相同的字节数组 const string s1 dg const string s2 dq byte a1 Convert FromBase64String s1 byte a2
  • 类模板参数推导 - clang 和 gcc 不同

    下面的代码使用 gcc 编译 但不使用 clang 编译 https godbolt org z ttqGuL template
  • 在 Windows 窗体中保存带有 Alpha 通道的单色位图会保存不同(错误)的颜色

    在 C NET 2 0 Windows 窗体 Visual Studio Express 2010 中 我保存由相同颜色组成的图像 Bitmap bitmap new Bitmap width height PixelFormat Form
  • C# 中通过 Process.Kill() 终止的进程的退出代码

    如果在我的 C 应用程序中 我正在创建一个可以正常终止或开始行为异常的子进程 在这种情况下 我通过调用 Process Kill 来终止它 但是 我想知道该进程是否已退出通常情况下 我知道我可以获得终止进程的错误代码 但是正常的退出代码是什
  • C++ OpenSSL 导出私钥

    到目前为止 我成功地使用了 SSL 但遇到了令人困惑的障碍 我生成了 RSA 密钥对 之前使用 PEM write bio RSAPrivateKey 来导出它们 然而 手册页声称该格式已经过时 实际上它看起来与通常的 PEM 格式不同 相
  • 创建链表而不将节点声明为指针

    我已经在谷歌和一些教科书上搜索了很长一段时间 我似乎无法理解为什么在构建链表时 节点需要是指针 例如 如果我有一个节点定义为 typedef struct Node int value struct Node next Node 为什么为了
  • 将多个表映射到实体框架中的单个实体类

    我正在开发一个旧数据库 该数据库有 2 个具有 1 1 关系的表 目前 我为每个定义的表定义了一种类型 1Test 1Result 我想将这些特定的表合并到一个类中 当前的类型如下所示 public class Result public
  • 如何设计以 char* 指针作为类成员变量的类?

    首先我想介绍一下我的情况 我写了一些类 将 char 指针作为私有类成员 而且这个项目有 GUI 所以当单击按钮时 某些函数可能会执行多次 这些类是设计的单班在项目中 但是其中的某些函数可以执行多次 然后我发现我的项目存在内存泄漏 所以我想
  • while 循环中的 scanf

    在这段代码中 scanf只工作一次 我究竟做错了什么 include
  • 链接器错误:已定义

    我尝试在 Microsoft Visual Studio 2012 中编译我的 Visual C 项目 使用 MFC 但出现以下错误 error LNK2005 void cdecl operator new unsigned int 2
  • 为什么编译时浮点计算可能不会得到与运行时计算相同的结果?

    In the speaker mentioned Compile time floating point calculations might not have the same results as runtime calculation
  • 将控制台重定向到 .NET 程序中的字符串

    如何重定向写入控制台的任何内容以写入字符串 对于您自己的流程 Console SetOut http msdn microsoft com en us library system console setout aspx并将其重定向到构建在
  • 基于 OpenCV 边缘的物体检测 C++

    我有一个应用程序 我必须检测场景中某些项目的存在 这些项目可以旋转并稍微缩放 更大或更小 我尝试过使用关键点检测器 但它们不够快且不够准确 因此 我决定首先使用 Canny 或更快的边缘检测算法 检测模板和搜索区域中的边缘 然后匹配边缘以查
  • C# 模拟VolumeMute按下

    我得到以下代码来模拟音量静音按键 DllImport coredll dll SetLastError true static extern void keybd event byte bVk byte bScan int dwFlags
  • 哪种 C 数据类型可以表示 40 位二进制数?

    我需要表示一个40位的二进制数 应该使用哪种 C 数据类型来处理这个问题 如果您使用的是 C99 或 C11 兼容编译器 则使用int least64 t以获得最大的兼容性 或者 如果您想要无符号类型 uint least64 t 这些都定
  • C++ 中类级 new 删除运算符的线程安全

    我在我的一门课程中重新实现了新 删除运算符 现在我正在使我的代码成为多线程 并想了解这些运算符是否也需要线程安全 我在某处读到 Visual Studio 中默认的 new delete 运算符是线程安全的 但这对于我的类的自定义 new

随机推荐