A generic std::atomic<T>
is required to have a T
that is Copy Constructible and Copy Assignable :
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, eg 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
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.
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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.