Skip to content

<iostream>: Bitmasks should tolerate unconstrained operator overloads #292

@StephanTLavavej

Description

@StephanTLavavej

This should compile, but doesn't:

C:\Temp>type meow.cpp
template <typename T, typename U> void operator&(T&&, U&&) = delete;
template <typename T, typename U> void operator|(T&&, U&&) = delete;
template <typename T, typename U> void operator^(T&&, U&&) = delete;
template <typename T, typename U> void operator&=(T&&, U&&) = delete;
template <typename T, typename U> void operator|=(T&&, U&&) = delete;
template <typename T, typename U> void operator^=(T&&, U&&) = delete;
template <typename T> void operator~(T&&) = delete;

#include <iostream>

int main() {
    std::cout << 1729 << "\n";
}

C:\Temp>cl /EHsc /nologo /W4 meow.cpp
meow.cpp
S:\msvc\binaries\amd64chk\inc\xlocale(1059): error C2280: 'void operator &<const std::_Codecvt_mode&,std::_Codecvt_mode>(T,U &&)': attempting to reference a deleted function
        with
        [
            T=const std::_Codecvt_mode &,
            U=std::_Codecvt_mode
        ]
meow.cpp(1): note: see declaration of 'operator &'
S:\msvc\binaries\amd64chk\inc\xlocale(1125): error C2280: 'void operator &<const std::_Codecvt_mode&,std::_Codecvt_mode>(T,U &&)': attempting to reference a deleted function
        with
        [
            T=const std::_Codecvt_mode &,
            U=std::_Codecvt_mode
        ]
meow.cpp(1): note: see declaration of 'operator &'
[...19 more errors...]

This happens when the STL uses an enum as a bitmask:

enum _Codecvt_mode { _Consume_header = 4, _Generate_header = 2 };

if ((_Mode & _Consume_header) != 0 && _Ch == 0xfeffu) { // drop header and retry

without defining dedicated bitmask operators:

STL/stl/inc/type_traits

Lines 2048 to 2089 in 58bb49d

#define _BITMASK_OPS(_BITMASK) \
_NODISCARD constexpr _BITMASK operator&(_BITMASK _Left, _BITMASK _Right) noexcept { /* return _Left & _Right */ \
using _IntTy = _STD underlying_type_t<_BITMASK>; \
return static_cast<_BITMASK>(static_cast<_IntTy>(_Left) & static_cast<_IntTy>(_Right)); \
} \
\
_NODISCARD constexpr _BITMASK operator|(_BITMASK _Left, _BITMASK _Right) noexcept { /* return _Left | _Right */ \
using _IntTy = _STD underlying_type_t<_BITMASK>; \
return static_cast<_BITMASK>(static_cast<_IntTy>(_Left) | static_cast<_IntTy>(_Right)); \
} \
\
_NODISCARD constexpr _BITMASK operator^(_BITMASK _Left, _BITMASK _Right) noexcept { /* return _Left ^ _Right */ \
using _IntTy = _STD underlying_type_t<_BITMASK>; \
return static_cast<_BITMASK>(static_cast<_IntTy>(_Left) ^ static_cast<_IntTy>(_Right)); \
} \
\
constexpr _BITMASK& operator&=(_BITMASK& _Left, _BITMASK _Right) noexcept { /* return _Left &= _Right */ \
return _Left = _Left & _Right; \
} \
\
constexpr _BITMASK& operator|=(_BITMASK& _Left, _BITMASK _Right) noexcept { /* return _Left |= _Right */ \
return _Left = _Left | _Right; \
} \
\
constexpr _BITMASK& operator^=(_BITMASK& _Left, _BITMASK _Right) noexcept { /* return _Left ^= _Right */ \
return _Left = _Left ^ _Right; \
} \
\
_NODISCARD constexpr _BITMASK operator~(_BITMASK _Left) noexcept { /* return ~_Left */ \
using _IntTy = _STD underlying_type_t<_BITMASK>; \
return static_cast<_BITMASK>(~static_cast<_IntTy>(_Left)); \
} \
\
_NODISCARD constexpr bool _Bitmask_includes( \
_BITMASK _Left, _BITMASK _Elements) noexcept { /* return (_Left & _Elements) != _BITMASK{} */ \
return (_Left & _Elements) != _BITMASK{}; \
} \
\
_NODISCARD constexpr bool _Bitmask_includes_all( \
_BITMASK _Left, _BITMASK _Elements) noexcept { /* return (_Left & _Elements) == _Elements */ \
return (_Left & _Elements) == _Elements; \
}

The test case has pathological overloads, but users can and have encountered this with reasonable code (typically, when they define templated operators to handle their own enums).

Curiously, this doesn't happen in /std:c++17 or /std:c++latest mode. I haven't analyzed why, but this is definitely a problem in /std:c++14 mode.

We should audit the entire STL for this problem.

Also tracked by Microsoft-internal VSO-115352 / AB#115352.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions