Section: 18.4.9 [concept.swappable] Status: New Submitter: Jiang An Opened: 2024-01-20 Last modified: 2024-03-15
Priority: 4
View other active issues in [concept.swappable].
View all other issues in [concept.swappable].
View all issues with New status.
Discussion:
18.4.9 [concept.swappable] bullet (2.3.1) currently requires T
to be a literal type in order
to make the swapping expression a constant expression in that case. The requirement was likely automatically
enforced by the core language rules in C++20 and thus essentially redundant.
T
is not a literal type.
E.g. the following program is accepted by GCC/libstdc++ in C++23 mode
(demo).
#include <concepts>
struct NonLiteral {
NonLiteral() {} // non-constexpr
constexpr NonLiteral(const NonLiteral&) noexcept {};
constexpr NonLiteral& operator=(const NonLiteral&) noexcept { return *this; };
};
int main()
{
NonLiteral x;
static_assert((std::ranges::swap(x, x), true));
}
IMO there's no good reason to additionally require literal types since C++23, which would complicate implementations.
[2024-03-15; Reflector poll]
Set priority to 4 after reflector poll.
Concerned about 7.7 [expr.const]/5.16 (can only modify non-volatile lvalues of literal type in constant expressions). Unable see a non-contrived case where this issue matters.
N.B. ranges::swap
needs the "reified object" treatment;
the repetitions of E1
and E2
are not pure textual repetitions of the
argument expressions.
Can we just eliminate all uses of "literal type"?
Wouldn't we still require a constexpr destructor?
Proposed resolution:
This wording is relative to N4971.
Modify 18.4.9 [concept.swappable] as indicated:
-2- The name
ranges::swap
denotes a customization point object (16.3.3.3.5 [customization.point.object]). The expressionranges::swap(E1, E2)
for subexpressionsE1
andE2
is expression-equivalent to an expressionS
determined as follows:
(2.1) — […]
(2.2) — […]
(2.3) — Otherwise, if
E1
andE2
are lvalues of the same typeT
that modelsmove_constructible<T>
andassignable_from<T&, T>
,S
is an expression that exchanges the denoted values.S
is a constant expression if
(2.3.1) —T
is a literal type (6.8.1 [basic.types.general]),(2.3.2) — both
E1 = std::move(E2)
andE2 = std::move(E1)
are constant subexpressions (3.14 [defns.const.subexpr]), and(2.3.3) — the full-expressions of the initializers in the declarations
T t1(std::move(E1)); T t2(std::move(E2));are constant subexpressions.
noexcept(S)
is equal tois_nothrow_move_constructible_v<T> && is_nothrow_move_assignable_v<T>
.(2.4) — Otherwise,
ranges::swap(E1, E2)
is ill-formed.[…]