jeudi 1 octobre 2020

std::optional: Not participating in overload resolution vs. being defined as deleted

I am trying to understand the mechanism behind type traits propagation as described for std::optional in http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0602r4.html. There is a subtle difference in the treatment of copy operations, which shall be conditionally defined as deleted, versus move operations, which shall rather not participate in overload resolution.

What is the reason for that difference, and how would I test the latter? Example:

#include <type_traits>
#include <optional>

struct NonMoveable {
  NonMoveable() = default;
  NonMoveable(NonMoveable const&) = default;
  NonMoveable(NonMoveable&&) = delete;
  NonMoveable& operator=(NonMoveable const&) = default;
  NonMoveable& operator=(NonMoveable&&) = delete;
};

// Inner traits as expected
static_assert(!std::is_move_constructible<NonMoveable>::value);
static_assert(!std::is_move_assignable<NonMoveable>::value);

// The wrapper is moveable, though. How to test that the corresponding special
// do not participate in overload resolution?
static_assert(std::is_move_constructible<std::optional<NonMoveable>>::value);
static_assert(std::is_move_assignable<std::optional<NonMoveable>>::value);

int main(int argc, char* argv[])
{
  NonMoveable a1;
  NonMoveable a2{std::move(a1)}; // Bad, as expected
  std::optional<NonMoveable> b1;
  std::optional<NonMoveable> b2{std::move(b1)}; // Good. Why?

  return 0;
}

Aucun commentaire:

Enregistrer un commentaire