这个函数调用应该不明确吗?



前几天我偶然发现了这个,无法弄清楚哪个答案是正确的,或者两者都可以接受。

具体来说,我指的是 OtherFunction 中对 bar(T{}) 的调用。从我在编译器资源管理器上测试的内容来看,这个决定似乎是分裂的。MSVC和ICC一致认为这是模棱两可的,而GCC和Clang编译代码没有问题。

隐藏的命名空间内的函数栏通过依赖于参数的查找变得可见。此外,msvc/icc 将全局命名空间中的 bar 声明视为候选,而 gcc/clang 则不然。似乎不应该考虑全局命名空间中的声明,因为它是在调用 bar(T{}) 之后声明的,但我不确定我是否正确阅读了非限定名称查找的规则,或者标准在这方面是否模棱两可。

https://godbolt.org/z/HAS-Cv

编辑: 看起来只要使用/permissive- 选项,msvc 就已经解决了这个问题 (https://devblogs.microsoft.com/cppblog/two-phase-name-lookup-support-comes-to-msvc/)

template <typename T>
inline void OtherFunction () {
bar(T{});
}
namespace hidden {
struct Foo {};
inline void bar (Foo foo) {}
}
inline void bar (hidden::Foo foo) {}
void Function () {
OtherFunction<hidden::Foo>();
}

Gcc 和 Clang 是正确的。在定义OtherFunction之后定义的全局bar无法通过名称查找找到;而hidden::bar可以通过ADL找到。

(强调我的)

对于模板定义中使用的依赖名称,查找将推迟到模板参数已知后,此时 ADL 检查从模板定义上下文以及模板实例化上下文中可见的函数声明with external linkage (until C++11),而非 ADL 查找仅检查从模板定义上下文中可见的函数声明with external linkage (until C++11)(换句话说,在模板定义之后添加新的函数声明不会使其可见,除非通过 ADL)。

代码有效,因此 msvc 和 icc 不正确。

由于bar参数与类型相关,因此名称bar是依赖名称,并且仅在实例化模板OtherFunction时查找,而不是在定义模板时查找。

C++17 [临时候选人]/1:

对于后缀表达式是依赖名称的函数调用,可以使用通常的查找规则([basic.lookup.unqual]、[basic.lookup.argdep])找到候选函数,但以下情况除外:

  • 对于使用非限定名称查找 ([basic.lookup.unqual]) 的查找部分,仅找到模板定义上下文中的函数声明。

  • 对于使用关联命名空间 ([basic.lookup.argdep]) 的查找部分,仅找到来自模板定义上下文或模板实例化上下文的函数声明。

所以跳到[basic.lookup.argdep]/3:

X是由非限定查找 ([basic.lookup.unqual]) 生成的查找集,设Y是由参数相关查找生成的查找集(定义如下)。如果X包含

  • 集体成员的声明,或
  • 不是using声明的块作用域函数声明,或
  • 既不是函数也不是函数模板的声明

Y为空。否则,Y是在与参数类型关联的命名空间中找到的声明集,如下所述。通过查找名称找到的声明集是XY的并集。

[目前的C++20草案重新安排了这些章节的措辞。特别是,有关在关联的命名空间中包括用于查找依赖名称的实例化上下文的规则现在列在 [basic.lookup.argdep]/4.5 中,并且只是 [temp.dep.candidate ] 中的一个注释。我不确定这样做的原因是否只是为了清楚起见,或者可能与模块的效果有关。

X是名称的非限定查找的结果,bar仅考虑模板定义上下文中可见的声明。但是,由于模板定义上下文是翻译单元的开头,因此显然 X是空的。

由于X根本不包含任何内容,因此它不包含列出的项目,这会强制Y为空。因此,为了确定Y,我们查看与参数类型关联的命名空间。这个实例化的参数类型是hidden::Foo,所以唯一关联的命名空间是hidden,名称查找的单一结果是函数hidden::bar

::bar在此名称查找中不可见,因此bar(T{})表达式不能不明确。

最新更新