TartanLlama / expected

C++11/14/17 std::expected with functional-style extensions

Home Page:https://tl.tartanllama.xyz

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

tl::expected forces copy construction on non-copyable types

E1pp opened this issue · comments

commented

Hi,

The following snippet doesn't compile on clang 14.0.6

#include "expected.hpp"

class MoveOnly{
 public:
  MoveOnly() = default;

  // Non-copyable
  MoveOnly(const MoveOnly&) = delete;
  MoveOnly& operator=(const MoveOnly&) = delete;

  // Movable trivially
  MoveOnly(MoveOnly&&) = default;
  MoveOnly& operator=(MoveOnly&&) = default;
};

int main(){
  tl::expected<MoveOnly, std::error_code> a{};
  tl::expected<MoveOnly, std::error_code> b = std::move(a); // CE
}

Produced error point to the following snippet in the source code

#ifndef TL_EXPECTED_GCC49
template <class T, class E,
          bool = is_void_or<T, std::is_trivially_move_constructible<T>>::value
              &&std::is_trivially_move_constructible<E>::value>
struct expected_move_base : expected_copy_base<T, E> {
  using expected_copy_base<T, E>::expected_copy_base;
};

(expected.hpp:936 and onward). This code never checks if T is not copyable which causes the issue in the first place. Definition rules of TL_EXPECTED_GCC49

#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 &&              \
     !defined(__clang__))
#define TL_EXPECTED_GCC49
#endif

#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 &&              \
     !defined(__clang__))
#define TL_EXPECTED_GCC54
#endif

#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 &&              \
     !defined(__clang__))
#define TL_EXPECTED_GCC55
#endif

doesn't do anything if clang is chosen as a compiler. I believe, adding rules for different clang versions (or at least some common denominator for features in clang and gcc) will fix this issue without breaking anything in the meantime.