【问题标题】:Permissible forms of out-of-class template member function definitions with trailing requires clauses带有尾随 requires 子句的类外模板成员函数定义的允许形式
【发布时间】:2022-11-25 06:47:57
【问题描述】:

考虑以下代码,其中类 A 具有嵌套类型 B,模板成员函数包含尾随 requires 子句命名嵌套类型 B,随后在类外定义:

template <typename X, typename Y>
concept C = true;

struct A
{
    struct B {};

    template <typename T>
    void f1()
    requires C<T, A::B>;

    template <typename T>
    void f2()
    requires C<T, A::B>;

    template <typename T>
    void f3()
    requires C<T, B>;

    template <typename T>
    void f4()
    requires C<T, B>;
};

template <typename T>
inline void A::f1()
requires C<T, A::B> {}

template <typename T>
inline void A::f2()
requires C<T, B> {}

template <typename T>
inline void A::f3()
requires C<T, A::B> {}

template <typename T>
inline void A::f4()
requires C<T, B> {}

int main()
{
    A{}.f1<A::B>();
    A{}.f2<A::B>(); 
    A{}.f3<A::B>();
    A{}.f4<A::B>();
}

我一直无法找到/理解关于是否:

  • 尾随 requires 子句可以以类似于尾随返回类型的方式命名嵌套类型而无需显式限定
  • f2f3f4 中的哪一个,如果有的话,应该被一致的实现所接受

我能在标准草案中找到的最接近的是 [temp.mem],

在其类模板定义之外定义的类模板的成员模板应指定一个与类模板等效的模板头,后跟一个与成员模板等效的模板头(13.7.6.1)。

13.7.6.1 在第 7 段中引用 [temp.over.link],

如果两个函数模板在同一范围内声明,具有相同的名称,具有等效的模板头,并且具有使用上述规则等效的返回类型、参数列表和尾随要求子句(如果有),则它们是等效的比较涉及模板参数的表达式。

就 requires 子句本身而言,等效性似乎由

它们都有 requires-clauses 并且相应的约束表达式是等价的。

在任何其他情况下,我希望 f1f4 中的所有形式的约束都是(正式的)相等的,但我对标准还不够熟悉,无法为自己得出结论。

在实现方面,clang 和 gcc 似乎始终接受所有定义,而 MSVC 不同,并且最近在行为上发生了变化:

Function gcc 12.2 clang 15.0.0 MSVC 19.33 MSVC Latest (19.34?)
f1 Accepted Accepted Accepted Accepted
f2 Accepted Accepted error C2244: 'A::f2': unable to match function definition to an existing declaration error C2244: 'A::f2': unable to match function definition to an existing declaration
f3 Accepted Accepted error C2244: 'A::f3': unable to match function definition to an existing declaration error C2244: 'A::f3': unable to match function definition to an existing declaration
f4 Accepted Accepted Accepted error C2065: 'B': undeclared identifier

【问题讨论】:

  • 可以在函数参数和 noexcept 说明符中使用不合格的 B(在模板和非模板中),我不明白为什么 requires 子句应该有任何不同。但我找不到标准在哪里这么说。
  • @n.m.我认为那部分应该由 eel.is/c++draft/basic.scope.class#1.sentence-2 处理,它在声明者编号在类的范围内。但是是否考虑C&lt;T, A::B&gt;C&lt;T, B&gt;相等的对我来说似乎不太清楚。
  • 在什么意义上它们可以是不等价的?
  • @n.m.根据 [temp.over.link] 中规定的规则。我试图尽我所能给出答案。

标签: c++ c++20 c++-concepts


【解决方案1】:

我引用的是当前草案而不是 C++20 草案,因为对相关部分进行了重大修订,使它们更加清晰。

根据[basic.scope.class]/1之后的一切声明者编号成员函数的(非friend)重新声明在类的范围内,因此应该能够使用非限定名称查找B。尾随要求子句出现在声明者编号A::fX

但是我认为因为模板参数T用于约束表达式sentence 1 of [temp.over.link]/2 apply 指出表达式 C&lt;T, A::B&gt;C&lt;T, B&gt; 只有在两个函数定义中使用它们时才等效,它们满足 ODR,除了模板参数可以重命名。但是 ODR 不允许使用不同的令牌序列来命名 A::B

但由于这两个表达式仍然功能等同这将使它们不匹配的情况病态的;无需诊断,这意味着 f2f3 将无效,尽管不需要诊断。

我认为等价规则有时很难遵循,所以我的看法可能不正确。但似乎 MSVC 正在遵循类似的解释,我怀疑最新版本中的 'B': undeclared identifier 错误只是一种回归。

【讨论】:

    猜你喜欢
    • 2020-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-20
    • 1970-01-01
    相关资源
    最近更新 更多