Pro*_*icT 13 c++ templates c++20
在 C++ 的 C++20 标准之前,当我们想要定义一个使用模板类的一些私有成员的类外运算符时,我们会使用类似于以下的构造:
template <typename T>
class Foo;
template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs);
template <typename T>
class Foo {
public:
constexpr Foo(T k) : mK(k) {}
constexpr friend bool operator==<T>(T lhs, const Foo& rhs);
private:
T mK;
};
template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs) {
return lhs == rhs.mK;
}
int main() {
return 1 == Foo<int>(1) ? 0 : 1;
}
Run Code Online (Sandbox Code Playgroud)
但是,从 C++20 开始,我们可以省略类外声明,因此也可以省略前向声明,因此我们可以只使用:
template <typename T>
class Foo {
public:
constexpr Foo(T k) : mK(k) {}
constexpr friend bool operator==<T>(T lhs, const Foo& rhs);
private:
T mK;
};
template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs) {
return lhs == rhs.mK;
}
Run Code Online (Sandbox Code Playgroud)
现在,我的问题是,C++20 的哪一部分允许我们这样做?为什么这在早期的 C++ 标准中是不可能的?
我提交了关于 gcc 的 bugzilla的错误报告
海湾合作委员会有一个错误。
始终对出现在 a 之前的模板名称执行名称查找<
,即使所讨论的名称是在(友元、显式专业化或显式实例化)声明中声明的名称。
由于友元声明中的名称operator==
是非限定名称,并且需要在模板中进行名称查找,因此适用两阶段名称查找规则。在此上下文中,operator==
不是从属名称(它不是函数调用的一部分,因此 ADL 不适用),因此会在名称出现的位置查找并绑定该名称(请参阅 [temp.nondep] 第 1 段)。您的示例格式不正确,因为此名称查找未找到 的声明operator==
。
我希望 GCC 在 C++20 模式下接受这一点,因为P0846R0允许(例如)operator==<T>(a, b)
在模板中使用,即使事先没有声明为operator==
模板可见。
这是一个更有趣的测试用例:
template <typename T> struct Foo;
#ifdef WRONG_DECL
template <typename T> bool operator==(Foo<T> lhs, int); // #1
#endif
template <typename T> struct Foo {
friend bool operator==<T>(Foo<T> lhs, float); // #2
};
template <typename T> bool operator==(Foo<T> lhs, float); // #3
Foo<int> f;
Run Code Online (Sandbox Code Playgroud)
对于-DWRONG_DECL
,GCC 和 Clang 一致认为该程序格式错误:在模板定义的上下文中,对友元声明 #2 进行不合格查找,找到声明 #1,该声明与 的实例化友元不匹配Foo<int>
。声明 #3 甚至不被考虑,因为模板中的不合格查找找不到它。
对于-UWRONG_DECL
,GCC(在 C++17 及更早版本中)和 Clang 一致认为该程序由于不同的原因而格式错误:对operator==
第 2 行的不合格查找什么也找不到。
但是对于-UWRONG_DECL
,C++20 模式下的 GCC 似乎认为operator==
#2 中的非限定查找失败是可以的(大概是由于 P0846R0),然后似乎从模板实例化上下文中重做查找,现在找到#3,在违反了模板的正常两阶段名称查找规则。