【问题标题】:C++ concept checking in incomplete-class context不完整类上下文中的 C++ 概念检查
【发布时间】:2021-10-01 21:23:15
【问题描述】:

请考虑一个 C++20 概念程序:

struct A {};

template<typename T>
concept DerivedOnceFromA = requires(T t) { { static_cast<const A&>(t) }; };

template<DerivedOnceFromA T>
struct B {};

struct C : A
{
    B<C> foo();
};

这里DerivedOnceFromA 的概念检查T 可以静态转换为AB 是遵循此概念的模板结构。而structC是从A派生出来的(所以概念满足),定义了一个返回B&lt;C&gt;的函数。

此代码被 GCC 和 MSVC 接受,但被 Clang 拒绝并出现错误:

constraints not satisfied for class template 'B' [with T = C]

演示:https://gcc.godbolt.org/z/7Tc7xdbeq

在类体内使用类作为概念模板参数是否合法(那么什么编译器是正确的)?

【问题讨论】:

  • 仅供参考:有许多种方法可以将类型转换为A const&amp;。继承不是唯一的方法。如果要检查继承,需要使用derived_from
  • 有趣的是,std::derived_from 对于两个编译器都失败了。
  • 不妨跟上潮流并提到标准概念消除了您的自定义概念(除了实施问题)。 template&lt;std::derived_from&lt;A&gt; T&gt; 也是如此(因为它要求 A 是 T 的公共且明确的基数)。

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


【解决方案1】:

合法吗?是的,在范围内。但你无法收集到任何有用的信息。

[class.mem.general]

6一个类的complete-class上下文是一个

  • 函数体([dcl.fct.def.general]),
  • 默认参数,
  • noexcept 说明符 ([except.spec]),或
  • 类成员规范中的默认成员初始值设定项。

7 一个类被认为是一个完全定义的对象类型 ([basic.types]) (或完整类型)在结束 } 的 类说明符。该类在其范围内被认为是完整的 完整的类上下文;否则被认为是不完整的 它自己的类成员规范。

所以你使用它的时候它仍然被认为是不完整的,只是你最多转发声明的一种类型。因此,无论启用强制转换的机制(继承或用户定义的转换运算符)如何,您都无法知道它是否可以转换为其他引用。即使借助像std::derived_from 这样的标准概念,也不可能知道不完整类型是否源自A。不完整类型的可观察属性非常有限。

【讨论】:

  • 谢谢。所以 Clang 就在这里拒绝代码?
  • @Fedor - 是的。不完整的类型不能满足约束(除了A 本身,整个T t 可能会被忽略)。
【解决方案2】:

在类的主体中,该类的类型不完整。

因此,您不能拥有依赖于完整类型的成员签名。

为了让您的代码能够正常工作,您需要将概念检查推迟到课程完成之后。一种方法是通过模板方法:

template<std::same_as<C> Y=C>
B<Y> foo()
{
    return {};
}

您甚至可以在 cpp 文件中实现 Y=C 的唯一有效特化。

这是一个有点愚蠢的解决方法,它确实会阻止虚拟方法。

【讨论】:

    猜你喜欢
    • 2021-08-29
    • 1970-01-01
    • 1970-01-01
    • 2020-11-29
    • 2013-11-25
    • 2020-10-28
    • 1970-01-01
    相关资源
    最近更新 更多