1

I was looking through some of the standard library's implementation for the usual containers (vector, unordered_map, etc...) when I came across the following, in the xutility header:

template <class _CtgIt, class _OutCtgIt>
_OutCtgIt _Copy_memmove(_CtgIt _First, _CtgIt _Last, _OutCtgIt _Dest) {
    auto _FirstPtr              = _To_address(_First);
    auto _LastPtr               = _To_address(_Last);
    auto _DestPtr               = _To_address(_Dest);
    const char* const _First_ch = const_cast<const char*>(reinterpret_cast<const volatile char*>(_FirstPtr));
    const char* const _Last_ch  = const_cast<const char*>(reinterpret_cast<const volatile char*>(_LastPtr));
    char* const _Dest_ch        = const_cast<char*>(reinterpret_cast<const volatile char*>(_DestPtr));
    const auto _Count           = static_cast<size_t>(_Last_ch - _First_ch);
    _CSTD memmove(_Dest_ch, _First_ch, _Count);
    if constexpr (is_pointer_v<_OutCtgIt>) {
        return reinterpret_cast<_OutCtgIt>(_Dest_ch + _Count);
    } else {
        return _Dest + (_LastPtr - _FirstPtr);
    }
}

Does anybody know why _First_ch and _Last_ch are first cast to const volatile char* type then immediately cast to const char*? I'm assuming it's to stop the compiler from optimizing prematurely, for some specific cases, but no concrete examples come to mind.

Sercan
  • 4,739
  • 3
  • 17
  • 36
Caetano
  • 117
  • 1
  • 7
  • 1
    Works around the problem of implementing vector without UB. See https://stackoverflow.com/questions/52996590/implementing-a-stdvector-like-container-without-undefined-behavior However, it's specific to MSVC. Library source code is not always portable to other compilers. – doug Jan 17 '22 at 05:06
  • ^This is very much in line with why I'm studying the implementation (for best practices on how to write containers and STL-like containers). Although I can't yet say whether this is one of the reasons why it's been implemented this way and @user17732522's answer seems like more immediate motivation, it still deserves +1 – Caetano Jan 17 '22 at 08:28
  • That's great. You can learn a lot looking at STL code but just be careful. Some of it can't be done without technical UB. Another example is the `std::complex` code where even using parts of it would cause UB except that compiler vendors are required to interpret what would otherwise be UB in a reasonable way. – doug Jan 17 '22 at 16:36

1 Answers1

2

If the target type of the pointer is volatile-qualified, it is not possible to use reinterpret_cast to directly cast to const char*.

reinterpret_cast is not allowed to cast away const or volatile. const_cast however can do this, while not being able to change the pointer's target type itself.

I think a C-style cast would also always work in this situation, but reasoning about it is a bit more difficult, since it attempts multiple C++-style conversion sequences, only the last of which is a reinterpret_cast followed by a const_cast.

It may be just a style choice to not use C-style casts here.

user17732522
  • 53,019
  • 2
  • 56
  • 105
  • This is indeed the case, const-casts and volatility are two things I rarely deal with in code. Just when you thought you knew everything there was to know. +1 – Caetano Jan 17 '22 at 08:31