exchange()
utility function, revision 3Revises N3511
by adding a default template argument
and N3608
by adding std::
qualifiers to the wording.
Atomic objects provide an atomic_exchange
function
([atomics.types.operations.req]p18) that assigns a new value to the object
and returns the old value. This operation is also useful on non-atomic
objects, and this paper proposes adding it to the library. The benefit
isn't huge, but neither is the specification cost.
template<typename T, typename U=T>
T exchange(T& obj, U&& new_val) {
T old_val = std::move(obj);
obj = std::forward<U>(new_val);
return old_val;
}
For primitive types, this is equivalent to the obvious implementation, while for more complex types, this definition
I chose the name for symmetry with atomic_exchange
, since
they behave the same except for this function not being atomic.
We could consider defaulting new_val
to T{}
,
which makes null'ing out a pointer even simpler, but I think that makes the
name less good, so I'm not proposing it.
c++std-ext-13750 mentions a use case for post-increment on
bool
, which this function would replace. This proposal comes
from my message c++std-lib-33183 on that thread.
auto operator << (std::ostream& os, section_num const & sn) -> std::ostream & {
if (!sn.prefix.empty()) { os << sn.prefix << " "; }
bool use_period{false};
for (auto sub : sn.num ) {
if(use_period++) { // <-- deprecated check to omit some code on the first time through the loop
os << '.';
}
if (sub >= 100) {
os << char(sub - 100 + 'A');
}
else {
os << sub;
}
}
return os;
}
becomes:
auto operator << (std::ostream& os, section_num const & sn) -> std::ostream & {
if (!sn.prefix.empty()) { os << sn.prefix << " "; }
bool use_period{false};
for (auto sub : sn.num ) {
if(std::exchange(use_period, true)) {
os << '.';
}
if (sub >= 100) {
os << char(sub - 100 + 'A');
}
else {
os << sub;
}
}
return os;
}
Implementations of std::unique_ptr
can also benefit:
template<typename T, typename D>
void unique_ptr<T, D>::reset(pointer p = pointer()) {
pointer old = ptr_;
ptr_ = p;
if (old)
deleter_(old);
}
becomes:
template<typename T, typename D>
void unique_ptr<T, D>::reset(pointer p = pointer()) {
if (pointer old = std::exchange(ptr_, p))
deleter_(old);
}
Giving the second template argument a default value fixes the following two cases:
DefaultConstructible x = ...;
if (exchange(x, {})) { ... }
int (*fp)(int);
int f(int);
double f(double);
/*...*/ exchange(fp, &f) /*...*/
Wording is relative to N3485.
// [utility.exchange] exchange:
template <class T, class U=T> T exchange(T& obj, U&& new_val);
template <class T, class U=T> T exchange(T& obj, U&& new_val);
Effects: Equivalent to:
T old_val = std::move(obj);
obj = std::forward<U>(new_val);
return old_val;