为什么 C++20 不使用 `requires` 来限制 atomic<T> 的 T?

Ami*_*rsh 3 c++ atomic c++-concepts c++20

一个通用的std::atomic<T>,需要有一个T是可复制构造和复制可分配:

[atomics.types.generic]/1

如果存在以下任一情况,则程序格式错误

(1.1) is_­trivially_­copyable_­v<T>,

(1.2) is_­copy_­constructible_­v<T>,

(1.3) is_­move_­constructible_­v<T>,

(1.4) is_­copy_­assignable_­v<T>,

或 (1.5) is_­move_­assignable_­v<T>

是false。

以上对于 C++20 来说并不陌生。编译器可能会static_assert为不符合标准的 T 发出错误。

但是,C++20 可以在语法中使用形式约束requires来正式要求上述内容作为类型的一部分,例如:

template< class T > requires
    std::is_trivially_copyable_v<T> &&
    std::is_copy_constructible_v<T> &&
    std::is_move_constructible_v<T> &&
    std::is_copy_assignable_v<T> &&
    std::is_move_assignable_v<T>
struct atomic { ... };
Run Code Online (Sandbox Code Playgroud)

C++20 是否有理由避免为此目的使用形式约束?


编辑: @TC在下面的回答中正确指出:

对于std::atomic特别是,限制主模板只是不是一种选择,给定atomic<shared_ptr<T>>和 atomic<weak_ptr<T>>特在C ++ 20添加的。

有一个选项建议:

也许你可以做一些更有趣的事情(比如一个未定义和不受约束的主模板加上一个受约束的部分特化),但它增加的价值很小。

好吧,还有另一种选择,不需要未定义和不受约束的主模板,这仍然有点复杂,并降低了使用此用法概念的价值和乐趣,但可能比未定义的基本模板更好:

template< class T > requires
    std::is_trivially_copyable_v<T> &&
    std::is_copy_constructible_v<T> &&
    std::is_move_constructible_v<T> &&
    std::is_copy_assignable_v<T> &&
    std::is_move_assignable_v<T>
    || std::same_as<T, std::shared_ptr<typename T::element_type>>
    || std::same_as<T, std::weak_ptr<typename T::element_type>>
struct atomic { ... };

template< class T >
struct atomic<std::shared_ptr<T>> { ... };

template< class T >
struct atomic<std::weak_ptr<T>> { ... };

// types of all other specializations are Copy Constructible and Copy Assignable
Run Code Online (Sandbox Code Playgroud)

代码:https : //godbolt.org/z/JaCu78

Bar*_*rry 8

库规范故意避免使用任何特定技术来实现其目标P0788:

四、让我们避免任何要求实现必须符合库规范的任何特定技术的规范。

a) 让我们允许实现使用requires 子句、 an enable_if、 aconstexpr if或任何其他技术或技术组合来满足约束:规范。

b) 让我们允许实现使用static_assert和/或任何其他技术来满足指令:规范。

c) 让我们允许实现使用合同属性 [P0542R1] 和/或任何其他技术来满足预期:和确保:规范。

d) 让我们考虑依赖于实现部分的任何特定技术的用户代码是格式错误的,不需要诊断。

这在P1369 中进行了扩展。

目标是避免将库的规范与它的任何特定实现联系起来。在某些情况下您确实需要这样做 - 许多范围的事情确实需要概念才能工作,因此它们以这种方式指定 - 但在大多数情况下,您不需要。

对于用户来说,重要的部分是对 的强制要求T。如何执行这些要求并不重要。它可能是一个概念,也可能是一个static_assert,也可能是一些编译器内在的东西,等等。