初始示例:
#include <ostream>
template<typename T>
void serialize(std::ostream& stream, const T& value) {}
template<typename T>
class Field
{
public:
void serialize(std::ostream& stream)
{
serialize(stream, m_value);
}
protected:
T m_value = {};
};
代码示例无法编译(gcc-9,std=c++2a)。但是,编译器输出表明该函数serialize(std::ostream&, const T&)
不能从给定的上下文中调用。将方法的主体更改为serialize
以下代码:
::serialize(stream, m_value);
一切正常。但是,对我来说,为什么会出现这样的问题并不明显——具有不同签名的函数会突然产生冲突。给出标准特定段落的链接。
PS我检查了一个非模板类 - 情况类似。
需要立即注意的是,您提供的代码没有立即出错,因为提供的模板代码没有实例化,也没有立即为其执行名称查找,因为内部调用
serialize
依赖于模板参数T
。对于特定的实例化,名称查找过程的结果serailize
理论上可以取决于特定类型T
(见下文)和执行实例化的位置。在您的示例中,这些都没有显示。但是从一般的角度来看,您会观察到经典的名称隐藏。嵌套范围内的任何名称都会隐藏封闭范围内的所有名称。
这种现象没有在标准中明确描述(或者甚至可能被描述,正如您自己在 中所说的那样
[basic.scope.hiding]
),但无论如何,它直接源于名称查找和重载解析是 C++ 中的两个独立步骤这一事实。首先,完成非限定名称查找,传统上执行由内向外自下而上的搜索,并在找到至少一个合适名称的范围内停止。然后,重载解决过程仅在该特定范围内找到的名称上运行。在这里,人们希望有一种依赖于参数的查找 (ADL),它具有正确的参数类型,
m_value
也可以看到全局声明serialize
。但是,该标准明确指出,如果正常名称查找发现类成员名称作为候选,则 ADL 将立即被忽略 ( http://eel.is/c++draft/basic.lookup.argdep#3.1 )。所以在你的情况下,ADL 没有帮助。在下面的示例中,名称隐藏也发生了,但隐藏不是由类方法执行的,而是由外部函数执行的
在这种情况下,ADL 不会被忽略。在此示例
T == int
中,ADL 什么也没找到,并且由于与您的示例中相同的原因,我们得到了相同的错误。但是使用T == S
ADL,它允许我们也找到一个全局serialize
的,错误就会消失。