模板类中的C ++ 20类外定义


12

直到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 ++标准中这不可能呢?


正如评论中指出的那样,clang不接受演示中提供的这段代码,这表明这实际上可能是gcc中的错误。

我提交了关于gcc的bugzilla 的错误报告


2
我个人更喜欢在类定义中使用,避免使用模板功能(并扣除“问题”(不匹配"c string" == Foo<std::string>("foo")))。
Jarod42

@ Jarod42我完全同意,我也更喜欢课堂定义。我很惊讶地发现C ++ 20允许我们在定义类输入时不重复函数签名三次,这对于在隐藏于.inl文件中的实现的公共API可能有用。
ProXicT

我没有注意到这是不可能的。到目前为止,我怎么会毫无问题地使用它?
ALX23z

1
嗯,在temp.friend中,变化不大,尤其是1.3不应对此行为负责。由于铛也不会接受您的代码,我倾向于GCC有一个bug。
n314159

@ ALX23z如果未对类进行模板化,则无需类外声明即可工作。
ProXicT

Answers:


2

GCC有一个错误。

始终对出现在之前的模板名称执行名称查找<,即使所讨论的名称是在(朋友,显式专门化或显式实例化)声明中声明的名称。

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

由于P0846R0,我希望GCC在C ++ 20模式下接受此设置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;

-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中找到了#3。违反了模板的常规两阶段名称查找规则。


感谢您的详细解释,非常好!
ProXicT
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.