问题
A generic std::atomic<T>
is required to have a T
that is Copy Constructible and Copy Assignable:
[atomics.types.generic]/1
The program is ill-formed if any of
(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>
,or (1.5)
is_move_assignable_v<T>
is
false
.
Above is not new to C++20. Compilers may use static_assert
to issue an error for a non-conforming T.
However, C++20 could use formal constraints with the requires
syntax to formally require the above as part of the type, e.g. something like:
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 { ... };
Is there a reason why C++20 refrained from using formal constraints for this purpose?
EDIT: @T.C. points out correctly, in an answer below:
For
std::atomic
in particular, constraining the primary template is simply not an option, given theatomic<shared_ptr<T>>
andatomic<weak_ptr<T>>
specializations that were added in C++20.
with an option suggesting that:
Perhaps you can do something fancier (like an undefined and unconstrained primary template plus a constrained partial specialization), but it adds very little value.
Well, there is another option, without the need for an undefined and unconstrained primary template, which is still a bit complex and reduces the value and fun in going with concepts for this usage, but probably better than an undefined base template:
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
Code: https://godbolt.org/z/JaCu78
回答1:
The library specification deliberately avoids using any particular technology to achieve its goals P0788:
IV. Let’s avoid any specification that demands any particular technology by which implementations must comply with Library specifications.
a) Let’s permit an implementation to use a requires-clause, an
enable_if
, aconstexpr if
, or any other technology or combination of technologies to meet Constraints: specifications.b) Let’s permit an implementation to use
static_assert
and/or any other technologies to meet Mandates: specifications.c) Let’s permit an implementation to use Contracts attributes [P0542R1] and/or any other technologies to meet Expects: and Ensures: specifications.
d) Let’s consider user code that relies on any specific technology on the part of an implementation to be ill-formed, with no diagnostic required.
Which is expanded upon in P1369.
The goal is to avoid tying the specification of the library to any particular implementation of it. There are cases where you do need to do this - many of the Ranges things do require concepts to work, so they are specified in this way - but for the most part, you don't.
For the user, the important part is the mandated requirements on T
. It's not important how those requirements are enforced. It could be a concept, it could be a static_assert
, it could be some compiler intrisic, whatever.
回答2:
For std::atomic
in particular, constraining the primary template is simply not an option, given the atomic<shared_ptr<T>>
and atomic<weak_ptr<T>>
specializations that were added in C++20.
Perhaps you can do something fancier (like an undefined and unconstrained primary template plus a constrained partial specialization), but it adds very little value.
来源:https://stackoverflow.com/questions/62154178/why-c20-doesnt-use-requires-to-restrict-the-t-for-atomict