A relative question, but I want a solution without any run-time overhead. (So constructing a new pair
or using std::variant
are not the answers)
Due to the potential template specialization, reference has said pair<K, V>
and pair<const K, V>
are not similar, that means a simple reinterpret_cast
would trigger undefined behaviour.
auto p1 = pair<int, double>{ 1, 2.3 };
auto& p2 = reinterpret_cast<pair<const int, double>&>(p1); // UB!
Type-punning through union
works fine in C, but not always legal in C++:
But there's an exception (to be consistent with behaviours in C?):
Since Key
and Value
may not be standard-layout
and may have non-trivial
destructor, it seems type-punning here is impossible, though members of pair<Key, Value>
and pair<const Key, Value>
could share the same lifetime (of course with alignment assertion).
template <typename Key, typename Value>
union MapPair {
using TrueType = pair<Key, Value>;
using AccessType = pair<const Key, Value>;
static_assert(
offsetof(TrueType, first) == offsetof(AccessType, first)
&& offsetof(TrueType, second) == offsetof(AccessType, second)
&& sizeof(TrueType) == sizeof(AccessType)
);
TrueType truePair;
AccessType accessPair;
~MapPair() {
truePair.~pair();
}
// constructors for `truePair`
};
//...
auto mapPair = MapPair<NonTrivialKey, NonTrivialValue>{/*...*/};
// UB? Due to the lifetime of `truepair` is not terminated?
auto& accessPair = reinterpret_cast<pair<const NonTrivialKey, NonTrivialValue>&>(mapPair);
// still UB? Although objects on the buffer share the same constructor/destructor and lifetime
auto* accessPairPtr = std::launder(reinterpret_cast<pair<const NonTrivialKey, NonTrivialValue>*>(&mapPair));
I've noticed the guarantee that no elements are copied or moved when calling std::map::extract
, and user-defined specilization of std::pair
would cause UB when operating Node handle
. So I trust some similar behaviours (type-punning or const_cast
) really exist in the STL implementations relating to Node handle
.
In libc++
, it seems to depend on the characteristic of clang
(doesn't optimize for data members), not the standard.
libstdc++
did the similar work as libc++
, but no std::launder
to refresh the type state.
MSVC
is ... very surprising... and the commit history is too short that I can't find any reasons to support such a simple aliasing...
Is there a standard way here?