tl::expected forces copy construction on non-copyable types
E1pp opened this issue · 0 comments
E1pp 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.