【问题标题】:C++20 out-of-class definition in a template class模板类中的 C++20 类外定义
【发布时间】:2020-06-02 07:26:09
【问题描述】:

直到 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;
}

然而,从 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;
}

Demo

现在,我的问题是,C++20 的哪一部分允许我们这样做?为什么在早期的 C++ 标准中不可能做到这一点?


正如 cmets 中所指出的,clang 不接受演示中提供的此代码,这表明这实际上可能是 gcc 中的错误。

我在 gcc 的 bugzilla 上提交了 bug report

【问题讨论】:

  • 我个人更喜欢在类定义中,避免模板函数(和推导“问题”(不匹配"c string" == Foo&lt;std::string&gt;("foo")))。
  • @Jarod42 我完全同意,我也更喜欢课堂定义。我只是惊讶地发现 C++20 允许我们在定义它的类中时不重复函数签名三次,这在实现位于隐藏 .inl 文件中的公共 API 中可能很有用。
  • 我没有注意到这是不可能的。为什么我使用它至今没有问题?
  • 嗯,在 temp.friend 中,没有太大变化,尤其是 1.3 应该对此行为负责。由于 clang 接受你的代码,我倾向于 gcc 有一个错误。
  • @ALX23z 如果类没有模板化,它可以在没有类外声明的情况下工作。

标签: c++ templates c++20


【解决方案1】:

GCC 有一个错误。

总是对出现在 &lt; 之前的模板名称执行名称查找,即使所讨论的名称是在(友元、显式特化或显式实例化)声明中声明的名称。

因为朋友声明中的名称operator== 是一个非限定名称,并且需要在模板中进行名称查找,所以适用两阶段名称查找规则。在这种情况下,operator== 不是从属名称(它不是函数调用的一部分,因此 ADL 不适用),因此在出现的位置查找并绑定名称(参见 [temp.nondep] 段落1)。您的示例格式错误,因为此名称查找未找到 operator== 的声明。

由于P0846R0,我希望 GCC 在 C++20 模式下接受这一点,它允许(例如)operator==&lt;T&gt;(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;

对于-DWRONG_DECL,GCC 和 Clang 同意这个程序是错误的:在模板定义的上下文中,对朋友声明 #2 的非限定查找找到了与实例化的不匹配的声明 #1 Foo&lt;int&gt; 的朋友。甚至不考虑声明 #3,因为模板中的非限定查找找不到它。

对于 -UWRONG_DECL,GCC(在 C++17 和更早版本中)和 Clang 同意该程序格式错误的原因不同:在第 2 行对 operator== 的非限定查找一无所获。

但是对于-UWRONG_DECL,C++20 模式下的 GCC 似乎认为 #2 中对 operator== 的非限定查找失败是可以的(可能是由于 P0846R0),然后似乎从模板重做查找实例化上下文,现在发现 #3,违反了模板的正常两阶段名称查找规则。

【讨论】:

  • 感谢您的详细解释,非常好!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-09
  • 2011-02-17
  • 2019-06-09
相关资源
最近更新 更多