libstdc++: Define std::expected for C++23 (P0323R12)
Because this adds a new class template called std::unexpected, we have to stop declaring the std::unexpected() function (which was deprecated in C++11 and removed in C++17). libstdc++-v3/ChangeLog: * doc/doxygen/user.cfg.in: Add new header. * include/Makefile.am: Likewise. * include/Makefile.in: Regenerate. * include/precompiled/stdc++.h: Add new header. * include/std/version (__cpp_lib_expected): Define. * libsupc++/exception [__cplusplus > 202002] (unexpected) (unexpected_handler, set_unexpected): Do not declare for C++23. * include/std/expected: New file. * testsuite/20_util/expected/assign.cc: New test. * testsuite/20_util/expected/cons.cc: New test. * testsuite/20_util/expected/illformed_neg.cc: New test. * testsuite/20_util/expected/observers.cc: New test. * testsuite/20_util/expected/requirements.cc: New test. * testsuite/20_util/expected/swap.cc: New test. * testsuite/20_util/expected/synopsis.cc: New test. * testsuite/20_util/expected/unexpected.cc: New test. * testsuite/20_util/expected/version.cc: New test.
This commit is contained in:
parent
d2906412ad
commit
b78e0ce28b
@ -858,6 +858,7 @@ INPUT = @srcdir@/doc/doxygen/doxygroups.cc \
|
||||
include/concepts \
|
||||
include/condition_variable \
|
||||
include/deque \
|
||||
include/expected \
|
||||
include/filesystem \
|
||||
include/forward_list \
|
||||
include/fstream \
|
||||
|
@ -42,6 +42,7 @@ std_headers = \
|
||||
${std_srcdir}/coroutine \
|
||||
${std_srcdir}/deque \
|
||||
${std_srcdir}/execution \
|
||||
${std_srcdir}/expected \
|
||||
${std_srcdir}/filesystem \
|
||||
${std_srcdir}/forward_list \
|
||||
${std_srcdir}/fstream \
|
||||
|
@ -400,6 +400,7 @@ std_headers = \
|
||||
${std_srcdir}/coroutine \
|
||||
${std_srcdir}/deque \
|
||||
${std_srcdir}/execution \
|
||||
${std_srcdir}/expected \
|
||||
${std_srcdir}/filesystem \
|
||||
${std_srcdir}/forward_list \
|
||||
${std_srcdir}/fstream \
|
||||
|
@ -153,5 +153,6 @@
|
||||
#endif
|
||||
|
||||
#if __cplusplus > 202002L
|
||||
#include <expected>
|
||||
#include <spanstream>
|
||||
#endif
|
||||
|
1240
libstdc++-v3/include/std/expected
Normal file
1240
libstdc++-v3/include/std/expected
Normal file
File diff suppressed because it is too large
Load Diff
@ -306,6 +306,7 @@
|
||||
|
||||
#if _GLIBCXX_HOSTED
|
||||
#define __cpp_lib_adaptor_iterator_pair_constructor 202106L
|
||||
#define __cpp_lib_expected 202202L
|
||||
#define __cpp_lib_invoke_r 202106L
|
||||
#define __cpp_lib_ios_noreplace 202200L
|
||||
#if __cpp_lib_concepts
|
||||
|
@ -79,7 +79,7 @@ namespace std
|
||||
* abandoned for any reason. It can also be called by the user. */
|
||||
void terminate() _GLIBCXX_USE_NOEXCEPT __attribute__ ((__noreturn__));
|
||||
|
||||
#if __cplusplus < 201703L || _GLIBCXX_USE_DEPRECATED
|
||||
#if __cplusplus < 201703L || (__cplusplus <= 202002L && _GLIBCXX_USE_DEPRECATED)
|
||||
/// If you write a replacement %unexpected handler, it must be of this type.
|
||||
typedef void (*_GLIBCXX11_DEPRECATED unexpected_handler) ();
|
||||
|
||||
|
321
libstdc++-v3/testsuite/20_util/expected/assign.cc
Normal file
321
libstdc++-v3/testsuite/20_util/expected/assign.cc
Normal file
@ -0,0 +1,321 @@
|
||||
// { dg-options "-std=gnu++23" }
|
||||
// { dg-do run { target c++23 } }
|
||||
|
||||
#include <expected>
|
||||
#include <type_traits>
|
||||
#include <testsuite_hooks.h>
|
||||
|
||||
int dtor_count;
|
||||
constexpr void reset_dtor_count()
|
||||
{
|
||||
if (!std::is_constant_evaluated())
|
||||
dtor_count = 0;
|
||||
}
|
||||
constexpr void inc_dtor_count()
|
||||
{
|
||||
if (!std::is_constant_evaluated())
|
||||
++dtor_count;
|
||||
}
|
||||
constexpr bool check_dtor_count(int c)
|
||||
{
|
||||
if (std::is_constant_evaluated())
|
||||
return true;
|
||||
return dtor_count == c;
|
||||
}
|
||||
|
||||
struct X
|
||||
{
|
||||
constexpr X(int i, int j = 0) noexcept : n(i+j) { }
|
||||
constexpr X(std::initializer_list<int> l, void*) noexcept : n(l.size()) { }
|
||||
|
||||
constexpr X(const X&) = default;
|
||||
constexpr X(X&& x) noexcept : n(x.n) { x.n = -1; }
|
||||
|
||||
constexpr X& operator=(const X&) = default;
|
||||
constexpr X& operator=(X&& x) noexcept { n = x.n; x.n = -1; return *this; }
|
||||
|
||||
constexpr ~X()
|
||||
{
|
||||
inc_dtor_count();
|
||||
}
|
||||
|
||||
constexpr bool operator==(const X&) const = default;
|
||||
constexpr bool operator==(int i) const { return n == i; }
|
||||
|
||||
int n;
|
||||
};
|
||||
|
||||
constexpr bool
|
||||
test_copy(bool = true)
|
||||
{
|
||||
reset_dtor_count();
|
||||
|
||||
std::expected<int, int> e1(1), e2(2), e3(std::unexpect, 3);
|
||||
|
||||
e1 = e1;
|
||||
e1 = e2; // T = T
|
||||
VERIFY( e1.value() == e2.value() );
|
||||
e1 = e3; // T = E
|
||||
VERIFY( ! e1.has_value() );
|
||||
VERIFY( e1.error() == e3.error() );
|
||||
e1 = e3; // E = E
|
||||
VERIFY( ! e1.has_value() );
|
||||
VERIFY( e1.error() == e3.error() );
|
||||
e1 = e2; // E = T
|
||||
VERIFY( e1.value() == e2.value() );
|
||||
|
||||
e1 = std::move(e1);
|
||||
e1 = std::move(e2); // T = T
|
||||
VERIFY( e1.value() == e2.value() );
|
||||
e1 = std::move(e3); // T = E
|
||||
VERIFY( ! e1.has_value() );
|
||||
VERIFY( e1.error() == e3.error() );
|
||||
e1 = std::move(e3); // E = E
|
||||
VERIFY( ! e1.has_value() );
|
||||
VERIFY( e1.error() == e3.error() );
|
||||
e1 = std::move(e2); // E = T
|
||||
VERIFY( e1.value() == e2.value() );
|
||||
|
||||
std::expected<X, X> x1(1), x2(2), x3(std::unexpect, 3);
|
||||
|
||||
x1 = x1;
|
||||
|
||||
x1 = x2; // T = T
|
||||
VERIFY( check_dtor_count(0) );
|
||||
VERIFY( x1.value() == x2.value() );
|
||||
x1 = x3; // T = E
|
||||
VERIFY( check_dtor_count(1) );
|
||||
VERIFY( ! x1.has_value() );
|
||||
x1 = x3; // E = E
|
||||
VERIFY( check_dtor_count(1) );
|
||||
VERIFY( ! x1.has_value() );
|
||||
x1 = x2; // E = T
|
||||
VERIFY( check_dtor_count(2) );
|
||||
VERIFY( x1.value() == x2.value() );
|
||||
|
||||
reset_dtor_count();
|
||||
|
||||
x1 = std::move(x1);
|
||||
VERIFY( x1.value() == -1 );
|
||||
|
||||
x1 = std::move(x2); // T = T
|
||||
VERIFY( check_dtor_count(0) );
|
||||
VERIFY( x1.value() == 2 );
|
||||
VERIFY( x2.value() == -1 );
|
||||
x1 = std::move(x3); // T = E
|
||||
VERIFY( check_dtor_count(1) );
|
||||
VERIFY( ! x1.has_value() );
|
||||
VERIFY( x1.error() == 3 );
|
||||
VERIFY( x3.error() == -1 );
|
||||
x3.error().n = 33;
|
||||
x1 = std::move(x3); // E = E
|
||||
VERIFY( check_dtor_count(1) );
|
||||
VERIFY( ! x1.has_value() );
|
||||
VERIFY( x1.error() == 33 );
|
||||
VERIFY( x3.error() == -1 );
|
||||
x2.value().n = 22;
|
||||
x1 = std::move(x2); // E = T
|
||||
VERIFY( check_dtor_count(2) );
|
||||
VERIFY( x1.value() == 22 );
|
||||
VERIFY( x2.value() == -1 );
|
||||
|
||||
std::expected<void, int> ev1, ev2, ev3(std::unexpect, 3);
|
||||
|
||||
ev1 = ev2; // T = T
|
||||
VERIFY( ev1.has_value() );
|
||||
ev1 = ev3; // T = E
|
||||
VERIFY( ! ev1.has_value() );
|
||||
VERIFY( ev1.error() == ev3.error() );
|
||||
ev1 = ev3; // E = E
|
||||
VERIFY( ! ev1.has_value() );
|
||||
VERIFY( ev1.error() == ev3.error() );
|
||||
ev1 = ev2; // E = T
|
||||
VERIFY( ev1.has_value() );
|
||||
|
||||
reset_dtor_count();
|
||||
std::expected<void, X> xv1, xv2, xv3(std::unexpect, 3);
|
||||
|
||||
xv1 = std::move(xv2); // T = T
|
||||
VERIFY( check_dtor_count(0) );
|
||||
VERIFY( xv1.has_value() );
|
||||
xv1 = std::move(xv3); // T = E
|
||||
VERIFY( check_dtor_count(0) );
|
||||
VERIFY( ! xv1.has_value() );
|
||||
VERIFY( xv1.error() == 3 );
|
||||
VERIFY( xv3.error() == -1 );
|
||||
xv3.error().n = 33;
|
||||
xv1 = std::move(xv3); // E = E
|
||||
VERIFY( check_dtor_count(0) );
|
||||
VERIFY( xv1.error() == 33 );
|
||||
VERIFY( xv3.error() == -1 );
|
||||
xv1 = std::move(xv2); // E = T
|
||||
VERIFY( check_dtor_count(1) );
|
||||
VERIFY( xv1.has_value() );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
test_converting(bool = true)
|
||||
{
|
||||
std::expected<int, int> e1(1);
|
||||
std::expected<unsigned, long> e2(2U), e3(std::unexpect, 3L);
|
||||
e1 = e2;
|
||||
VERIFY( e1.value() == e2.value() );
|
||||
e1 = e3;
|
||||
VERIFY( ! e1.has_value() );
|
||||
VERIFY( e1.error() == e3.error() );
|
||||
e1 = e2;
|
||||
VERIFY( e1.value() == e2.value() );
|
||||
|
||||
e1 = std::move(e3);
|
||||
VERIFY( ! e1.has_value() );
|
||||
VERIFY( e1.error() == e3.error() );
|
||||
e1 = std::move(e2);
|
||||
VERIFY( e1.value() == e2.value() );
|
||||
|
||||
std::expected<void, int> ev4;
|
||||
std::expected<const void, long> ev5(std::unexpect, 5);
|
||||
ev4 = ev5;
|
||||
VERIFY( ! ev4.has_value() );
|
||||
VERIFY( ev4.error() == 5 );
|
||||
ev4 = std::expected<volatile void, unsigned>();
|
||||
VERIFY( ev4.has_value() );
|
||||
ev4 = std::move(ev5);
|
||||
VERIFY( ! ev4.has_value() );
|
||||
VERIFY( ev4.error() == 5 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
test_unexpected(bool = true)
|
||||
{
|
||||
reset_dtor_count();
|
||||
|
||||
std::expected<X, int> e1(0);
|
||||
|
||||
e1 = std::unexpected<int>(5);
|
||||
VERIFY( ! e1.has_value() );
|
||||
VERIFY( e1.error() == 5 );
|
||||
VERIFY( check_dtor_count(1) );
|
||||
|
||||
e1 = std::unexpected<int>(6);
|
||||
VERIFY( check_dtor_count(1) );
|
||||
|
||||
std::expected<int, X> e2;
|
||||
|
||||
std::unexpected<X> x(std::in_place, 1, 2);
|
||||
e2 = x;
|
||||
VERIFY( check_dtor_count(1) );
|
||||
|
||||
e2 = 1;
|
||||
VERIFY( e2.value() == 1 );
|
||||
VERIFY( check_dtor_count(2) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
test_emplace(bool = true)
|
||||
{
|
||||
reset_dtor_count();
|
||||
|
||||
std::expected<int, int> e1(1);
|
||||
e1.emplace(2);
|
||||
VERIFY( e1.value() == 2 );
|
||||
|
||||
std::expected<void, int> ev2;
|
||||
ev2.emplace();
|
||||
VERIFY( ev2.has_value() );
|
||||
|
||||
std::expected<X, int> e3(std::in_place, 0, 0);
|
||||
|
||||
e3.emplace({1,2,3}, nullptr);
|
||||
VERIFY( e3.value() == 3 );
|
||||
VERIFY( check_dtor_count(1) );
|
||||
|
||||
e3.emplace(2, 2);
|
||||
VERIFY( e3.value() == 4 );
|
||||
VERIFY( check_dtor_count(2) );
|
||||
|
||||
std::expected<int, X> ev4(std::unexpect, 4);
|
||||
|
||||
ev4.emplace(5);
|
||||
VERIFY( ev4.value() == 5 );
|
||||
VERIFY( check_dtor_count(3) );
|
||||
|
||||
ev4.emplace(6);
|
||||
VERIFY( ev4.value() == 6 );
|
||||
VERIFY( check_dtor_count(3) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
test_exception_safety()
|
||||
{
|
||||
struct CopyThrows
|
||||
{
|
||||
CopyThrows(int i) noexcept : x(i) { }
|
||||
CopyThrows(const CopyThrows&) { throw 1; }
|
||||
CopyThrows(CopyThrows&&) = default;
|
||||
CopyThrows& operator=(const CopyThrows&) = default;
|
||||
CopyThrows& operator=(CopyThrows&&) = default;
|
||||
int x;
|
||||
|
||||
bool operator==(int i) const { return x == i; }
|
||||
};
|
||||
|
||||
struct MoveThrows
|
||||
{
|
||||
MoveThrows(int i) noexcept : x(i) { }
|
||||
MoveThrows(const MoveThrows&) = default;
|
||||
MoveThrows(MoveThrows&&) { throw 1L; }
|
||||
MoveThrows& operator=(const MoveThrows&) = default;
|
||||
MoveThrows& operator=(MoveThrows&&) = default;
|
||||
int x;
|
||||
|
||||
bool operator==(int i) const { return x == i; }
|
||||
};
|
||||
|
||||
std::expected<CopyThrows, MoveThrows> c(std::unexpect, 1);
|
||||
|
||||
// operator=(U&&)
|
||||
try {
|
||||
CopyThrows x(2);
|
||||
c = x;
|
||||
VERIFY( false );
|
||||
} catch (int) {
|
||||
VERIFY( ! c.has_value() );
|
||||
VERIFY( c.error() == 1 );
|
||||
}
|
||||
|
||||
c = CopyThrows(2);
|
||||
|
||||
try {
|
||||
c = std::unexpected<MoveThrows>(3);
|
||||
VERIFY( false );
|
||||
} catch (long) {
|
||||
VERIFY( c.value() == 2 );
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char**)
|
||||
{
|
||||
bool non_constant = argc == 1; // force non-constant evaluation
|
||||
|
||||
static_assert( test_copy() );
|
||||
test_copy(non_constant);
|
||||
static_assert( test_converting() );
|
||||
test_converting(non_constant);
|
||||
static_assert( test_unexpected() );
|
||||
test_unexpected(non_constant);
|
||||
static_assert( test_emplace() );
|
||||
test_emplace(non_constant);
|
||||
|
||||
test_exception_safety();
|
||||
|
||||
// Ensure the non-constexpr tests actually ran:
|
||||
VERIFY( dtor_count != 0 );
|
||||
}
|
175
libstdc++-v3/testsuite/20_util/expected/cons.cc
Normal file
175
libstdc++-v3/testsuite/20_util/expected/cons.cc
Normal file
@ -0,0 +1,175 @@
|
||||
// { dg-options "-std=gnu++23" }
|
||||
// { dg-do run { target c++23 } }
|
||||
|
||||
#include <expected>
|
||||
#include <testsuite_hooks.h>
|
||||
|
||||
constexpr bool
|
||||
test_default()
|
||||
{
|
||||
std::expected<int, int> e;
|
||||
VERIFY( e.has_value() );
|
||||
VERIFY( *e == 0 );
|
||||
|
||||
std::expected<void, int> ev;
|
||||
VERIFY( ev.has_value() );
|
||||
VERIFY( (ev.value(), true) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
test_val()
|
||||
{
|
||||
std::expected<int, int> e1(1);
|
||||
VERIFY( e1.has_value() );
|
||||
VERIFY( *e1 == 1 );
|
||||
|
||||
std::expected<int, int> e2(std::in_place, 2);
|
||||
VERIFY( e2.has_value() );
|
||||
VERIFY( *e2 == 2 );
|
||||
|
||||
struct X
|
||||
{
|
||||
constexpr X(std::initializer_list<int> l, void*) : n(l.size()) { }
|
||||
int n;
|
||||
};
|
||||
|
||||
std::expected<X, int> e3(X{{1, 2, 3}, nullptr});
|
||||
VERIFY( e3.has_value() );
|
||||
VERIFY( e3->n == 3 );
|
||||
|
||||
std::expected<X, int> e4(std::in_place, {1, 2, 3, 4}, nullptr);
|
||||
VERIFY( e4.has_value() );
|
||||
VERIFY( e4->n == 4 );
|
||||
|
||||
std::expected<void, int> ev(std::in_place);
|
||||
VERIFY( ev.has_value() );
|
||||
VERIFY( (ev.value(), true) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
test_err()
|
||||
{
|
||||
std::expected<int, int> e1(std::unexpected<int>(1));
|
||||
VERIFY( ! e1.has_value() );
|
||||
VERIFY( e1.error() == 1 );
|
||||
|
||||
const std::unexpected<int> u2(2);
|
||||
std::expected<int, int> e2(u2);
|
||||
VERIFY( ! e2.has_value() );
|
||||
VERIFY( e2.error() == 2 );
|
||||
|
||||
std::expected<int, int> e3(std::unexpect, 3);
|
||||
VERIFY( ! e3.has_value() );
|
||||
VERIFY( e3.error() == 3 );
|
||||
|
||||
struct X
|
||||
{
|
||||
constexpr X(int i, int j) : n(i+j) { }
|
||||
constexpr X(std::initializer_list<int> l, void*) : n(l.size()) { }
|
||||
int n;
|
||||
};
|
||||
|
||||
std::expected<int, X> e4(std::unexpect, 1, 3);
|
||||
VERIFY( ! e4.has_value() );
|
||||
VERIFY( e4.error().n == 4 );
|
||||
|
||||
std::expected<int, X> e5(std::unexpect, {1, 2, 3, 4, 5}, nullptr);
|
||||
VERIFY( ! e5.has_value() );
|
||||
VERIFY( e5.error().n == 5 );
|
||||
|
||||
std::expected<const void, int> ev1(std::unexpected<int>(1));
|
||||
VERIFY( ! ev1.has_value() );
|
||||
VERIFY( ev1.error() == 1 );
|
||||
|
||||
std::expected<volatile void, int> ev2(u2);
|
||||
VERIFY( ! ev2.has_value() );
|
||||
VERIFY( ev2.error() == 2 );
|
||||
|
||||
std::expected<const volatile void, int> ev3(std::unexpect, 3);
|
||||
VERIFY( ! ev3.has_value() );
|
||||
VERIFY( ev3.error() == 3 );
|
||||
|
||||
std::expected<void, X> ev4(std::unexpect, 1, 3);
|
||||
VERIFY( ! ev4.has_value() );
|
||||
VERIFY( ev4.error().n == 4 );
|
||||
|
||||
std::expected<void, X> ev5(std::unexpect, {1, 2, 3, 4, 5}, nullptr);
|
||||
VERIFY( ! ev5.has_value() );
|
||||
VERIFY( ev5.error().n == 5 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
test_copy()
|
||||
{
|
||||
std::expected<int, int> e1(1);
|
||||
std::expected<int, int> e2(e1);
|
||||
VERIFY( e2.value() == 1 );
|
||||
std::expected<int, int> e3(std::move(e2));
|
||||
VERIFY( e2.value() == 1 );
|
||||
VERIFY( e3.value() == 1 );
|
||||
std::expected<short, short> e4(e1);
|
||||
VERIFY( e4.value() == 1 );
|
||||
std::expected<short, short> e5(std::move(e4));
|
||||
VERIFY( e4.value() == 1 );
|
||||
VERIFY( e5.value() == 1 );
|
||||
|
||||
std::expected<int, int> u1(std::unexpect, 2);
|
||||
std::expected<int, int> u2(u1);
|
||||
VERIFY( ! u2.has_value() );
|
||||
VERIFY( u2.error() == 2 );
|
||||
std::expected<int, int> u3(std::move(u2));
|
||||
VERIFY( ! u3.has_value() );
|
||||
VERIFY( u3.error() == 2 );
|
||||
std::expected<short, short> u4(u1);
|
||||
VERIFY( ! u4.has_value() );
|
||||
VERIFY( u4.error() == 2 );
|
||||
std::expected<short, short> u5(std::move(u4));
|
||||
VERIFY( ! u5.has_value() );
|
||||
VERIFY( u5.error() == 2 );
|
||||
|
||||
std::expected<void, int> ev1;
|
||||
std::expected<void, int> ev2(ev1);
|
||||
VERIFY( ev2.has_value() );
|
||||
std::expected<void, int> ev3(std::move(ev2));
|
||||
VERIFY( ev2.has_value() );
|
||||
VERIFY( ev3.has_value() );
|
||||
std::expected<volatile void, short> ev4(ev1);
|
||||
VERIFY( ev4.has_value() );
|
||||
std::expected<const void, short> ev5(std::move(ev4));
|
||||
VERIFY( ev4.has_value() );
|
||||
VERIFY( ev5.has_value() );
|
||||
|
||||
std::expected<void, int> uv1(std::unexpect, 2);
|
||||
std::expected<void, int> uv2(uv1);
|
||||
VERIFY( ! uv2.has_value() );
|
||||
VERIFY( uv2.error() == 2 );
|
||||
std::expected<void, int> uv3(std::move(uv2));
|
||||
VERIFY( ! uv3.has_value() );
|
||||
VERIFY( uv3.error() == 2 );
|
||||
std::expected<const void, short> uv4(uv1);
|
||||
VERIFY( ! uv4.has_value() );
|
||||
VERIFY( uv4.error() == 2 );
|
||||
std::expected<volatile void, short> uv5(std::move(uv4));
|
||||
VERIFY( ! uv5.has_value() );
|
||||
VERIFY( uv5.error() == 2 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_default();
|
||||
static_assert( test_default() );
|
||||
test_val();
|
||||
static_assert( test_val() );
|
||||
test_err();
|
||||
static_assert( test_err() );
|
||||
test_copy();
|
||||
static_assert( test_copy() );
|
||||
}
|
67
libstdc++-v3/testsuite/20_util/expected/illformed_neg.cc
Normal file
67
libstdc++-v3/testsuite/20_util/expected/illformed_neg.cc
Normal file
@ -0,0 +1,67 @@
|
||||
// { dg-options "-std=gnu++23" }
|
||||
// { dg-do compile { target c++23 } }
|
||||
|
||||
#include <expected>
|
||||
|
||||
void
|
||||
test_unexpected()
|
||||
{
|
||||
int i[2]{};
|
||||
|
||||
// std::unexpected<E> is ill-formed if E is a non-object type,
|
||||
|
||||
std::unexpected<int&> ref(i[0]); // { dg-error "here" }
|
||||
std::unexpected<void()> func(test_unexpected); // { dg-error "here" }
|
||||
// { dg-error "no matching function for call to" "" { target *-*-* } 0 }
|
||||
// { dg-error "invalidly declared function type" "" { target *-*-* } 0 }
|
||||
|
||||
// an array type,
|
||||
std::unexpected<int[2]> array(i); // { dg-error "here" }
|
||||
|
||||
// a specialization of std::unexpected,
|
||||
std::unexpected<int> u(1);
|
||||
std::unexpected<std::unexpected<int>> nested(u); // { dg-error "here" }
|
||||
//
|
||||
// or a cv-qualified type.
|
||||
std::unexpected<const int> c_int(1); // { dg-error "here" }
|
||||
std::unexpected<volatile int> v_int(1); // { dg-error "here" }
|
||||
}
|
||||
|
||||
void
|
||||
test_expected_value()
|
||||
{
|
||||
// std::expected<T, E> is ill-formed if T is a reference type,
|
||||
std::expected<int&, int> ref(std::unexpect); // { dg-error "here" }
|
||||
// { dg-error "reference type" "" { target *-*-* } 0 }
|
||||
|
||||
// a function type,
|
||||
std::expected<void(), int> func(std::unexpect); // { dg-error "here" }
|
||||
// { dg-error "returning a function" "" { target *-*-* } 0 }
|
||||
//
|
||||
// possibly cv-qualified types in_place_t,
|
||||
std::expected<std::in_place_t, int> tag(std::unexpect); // { dg-error "here" }
|
||||
std::expected<const std::in_place_t, int> ctag(std::unexpect); // { dg-error "here" }
|
||||
// unexpect_t,
|
||||
std::expected<std::unexpect_t, int> utag(std::unexpect); // { dg-error "here" }
|
||||
std::expected<const std::unexpect_t, int> cutag(std::unexpect); // { dg-error "here" }
|
||||
// or a specialization of unexpected.
|
||||
std::expected<std::unexpected<int>, int> unex(std::in_place, 1); // { dg-error "here" }
|
||||
std::expected<const std::unexpected<int>, int> cunex(std::in_place, 1); // { dg-error "here" }
|
||||
}
|
||||
|
||||
void
|
||||
test_expected_error()
|
||||
{
|
||||
|
||||
// std::expected<T, E> is ill-formed if std::unexpected<E> would be
|
||||
// ill-formed. Test the same types as in test_unexpected().
|
||||
|
||||
std::expected<int, int&> ref; // { dg-error "here" }
|
||||
std::expected<int, void()> func; // { dg-error "here" }
|
||||
std::expected<int, int[2]> array; // { dg-error "here" }
|
||||
std::expected<int, std::unexpected<int>> nested; // { dg-error "here" }
|
||||
std::expected<int, const int> c_int; // { dg-error "here" }
|
||||
std::expected<int, volatile int> v_int; // { dg-error "here" }
|
||||
}
|
||||
|
||||
// { dg-prune-output "static assertion failed" }
|
209
libstdc++-v3/testsuite/20_util/expected/observers.cc
Normal file
209
libstdc++-v3/testsuite/20_util/expected/observers.cc
Normal file
@ -0,0 +1,209 @@
|
||||
// { dg-options "-std=gnu++23" }
|
||||
// { dg-do run { target c++23 } }
|
||||
|
||||
#include <expected>
|
||||
#include <expected>
|
||||
#include <testsuite_hooks.h>
|
||||
|
||||
struct X
|
||||
{
|
||||
constexpr int f() & { return 1; }
|
||||
constexpr int f() const & { return 2; }
|
||||
constexpr int f() && { return 3; }
|
||||
constexpr int f() const && { return 4; }
|
||||
};
|
||||
|
||||
constexpr bool
|
||||
test_arrow()
|
||||
{
|
||||
std::expected<X, int> e1;
|
||||
VERIFY( e1->f() == 1 );
|
||||
const auto& e2 = e1;
|
||||
VERIFY( e2->f() == 2 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
test_star()
|
||||
{
|
||||
std::expected<X, int> e1;
|
||||
VERIFY( (*e1).f() == 1 );
|
||||
VERIFY( std::move(*e1).f() == 3 );
|
||||
const auto& e2 = e1;
|
||||
VERIFY( (*e2).f() == 2 );
|
||||
VERIFY( std::move(*e2).f() == 4 );
|
||||
|
||||
std::expected<void, int> v;
|
||||
*v;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
test_has_value()
|
||||
{
|
||||
std::expected<int, int> e;
|
||||
VERIFY( e.has_value() );
|
||||
VERIFY( e );
|
||||
e = std::unexpected(1);
|
||||
VERIFY( ! e.has_value() );
|
||||
VERIFY( ! e );
|
||||
|
||||
std::expected<void, int> v;
|
||||
VERIFY( v.has_value() );
|
||||
VERIFY( v );
|
||||
v = std::unexpected(1);
|
||||
VERIFY( ! v.has_value() );
|
||||
VERIFY( ! v );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
test_value()
|
||||
{
|
||||
std::expected<X, int> e1;
|
||||
|
||||
VERIFY( e1.value().f() == 1 );
|
||||
VERIFY( std::move(e1).value().f() == 3 );
|
||||
const auto& e2 = e1;
|
||||
VERIFY( e2.value().f() == 2 );
|
||||
VERIFY( std::move(e2).value().f() == 4 );
|
||||
|
||||
std::expected<void, int> v1;
|
||||
v1.value();
|
||||
std::move(v1).value();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
test_value_throw()
|
||||
{
|
||||
std::expected<int, int> e1 = std::unexpected(9);
|
||||
|
||||
try {
|
||||
e1.value();
|
||||
VERIFY( false );
|
||||
} catch (const std::bad_expected_access<int>& e) {
|
||||
VERIFY( e.error() == 9 );
|
||||
}
|
||||
try {
|
||||
std::move(e1).value();
|
||||
VERIFY( false );
|
||||
} catch (const std::bad_expected_access<int>& e) {
|
||||
VERIFY( e.error() == 9 );
|
||||
}
|
||||
|
||||
const auto& e2 = e1;
|
||||
try {
|
||||
e2.value();
|
||||
VERIFY( false );
|
||||
} catch (const std::bad_expected_access<int>& e) {
|
||||
VERIFY( e.error() == 9 );
|
||||
}
|
||||
try {
|
||||
std::move(e2).value();
|
||||
VERIFY( false );
|
||||
} catch (const std::bad_expected_access<int>& e) {
|
||||
VERIFY( e.error() == 9 );
|
||||
}
|
||||
|
||||
std::expected<void, int> v1 = std::unexpected(8);
|
||||
try {
|
||||
v1.value();
|
||||
VERIFY( false );
|
||||
} catch (const std::bad_expected_access<int>& e) {
|
||||
VERIFY( e.error() == 8 );
|
||||
}
|
||||
try {
|
||||
std::move(v1).value();
|
||||
VERIFY( false );
|
||||
} catch (const std::bad_expected_access<int>& e) {
|
||||
VERIFY( e.error() == 8 );
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
test_error()
|
||||
{
|
||||
std::expected<int, X> e1(std::unexpect);
|
||||
|
||||
VERIFY( e1.error().f() == 1 );
|
||||
VERIFY( std::move(e1).error().f() == 3 );
|
||||
const auto& e2 = e1;
|
||||
VERIFY( e2.error().f() == 2 );
|
||||
VERIFY( std::move(e2).error().f() == 4 );
|
||||
|
||||
std::expected<void, X> v1(std::unexpect);
|
||||
|
||||
VERIFY( v1.error().f() == 1 );
|
||||
VERIFY( std::move(v1).error().f() == 3 );
|
||||
const auto& v2 = v1;
|
||||
VERIFY( v2.error().f() == 2 );
|
||||
VERIFY( std::move(v2).error().f() == 4 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
test_value_or()
|
||||
{
|
||||
struct Movable
|
||||
{
|
||||
constexpr Movable(int i) : x(i) { }
|
||||
constexpr Movable(const Movable&) = default;
|
||||
constexpr Movable(Movable&& m) : x(m.x) { m.x = -1; }
|
||||
int x;
|
||||
|
||||
constexpr bool operator==(int i) const { return x == i; }
|
||||
};
|
||||
|
||||
std::expected<Movable, int> e1(1);
|
||||
|
||||
Movable m2(2);
|
||||
VERIFY( e1.value_or(2) == 1 );
|
||||
VERIFY( e1.value_or(m2) == 1 );
|
||||
VERIFY( e1.value_or(std::move(m2)) == 1 );
|
||||
VERIFY( m2 == 2 );
|
||||
|
||||
VERIFY( std::move(e1).value_or(m2) == 1 );
|
||||
VERIFY( *e1 == -1 ); // moved
|
||||
VERIFY( m2 == 2 );
|
||||
|
||||
e1 = std::unexpected(3);
|
||||
VERIFY( e1.value_or(m2) == 2 );
|
||||
VERIFY( m2 == 2 );
|
||||
VERIFY( std::move(e1).value_or(m2) == 2 );
|
||||
VERIFY( m2 == 2 );
|
||||
|
||||
VERIFY( e1.value_or(std::move(m2)) == 2 );
|
||||
VERIFY( m2 == -1 );
|
||||
|
||||
m2.x = 4;
|
||||
VERIFY( std::move(e1).value_or(std::move(m2)) == 4 );
|
||||
VERIFY( m2 == -1 );
|
||||
|
||||
VERIFY( e1.value_or(5) == 5 );
|
||||
VERIFY( std::move(e1).value_or(6) == 6 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
static_assert( test_arrow() );
|
||||
test_arrow();
|
||||
static_assert( test_star() );
|
||||
test_star();
|
||||
static_assert( test_has_value() );
|
||||
test_has_value();
|
||||
static_assert( test_value() );
|
||||
test_value();
|
||||
test_value_throw();
|
||||
static_assert( test_error() );
|
||||
test_error();
|
||||
static_assert( test_value_or() );
|
||||
test_value_or();
|
||||
}
|
129
libstdc++-v3/testsuite/20_util/expected/requirements.cc
Normal file
129
libstdc++-v3/testsuite/20_util/expected/requirements.cc
Normal file
@ -0,0 +1,129 @@
|
||||
// { dg-options "-std=gnu++23" }
|
||||
// { dg-do compile { target c++23 } }
|
||||
|
||||
#include <expected>
|
||||
#include <type_traits>
|
||||
|
||||
// Default construction
|
||||
|
||||
template<typename T, typename E>
|
||||
constexpr bool default_constructible
|
||||
= std::is_default_constructible_v<std::expected<T, E>>;
|
||||
|
||||
struct A { A(int); };
|
||||
|
||||
static_assert( default_constructible< int, int > );
|
||||
static_assert( default_constructible< A, int > == false );
|
||||
static_assert( default_constructible< int, A > );
|
||||
static_assert( default_constructible< A, A > == false );
|
||||
static_assert( default_constructible< int, A > );
|
||||
static_assert( default_constructible< void, int > );
|
||||
|
||||
// Destruction
|
||||
|
||||
template<typename T, typename E>
|
||||
constexpr bool trivially_destructible
|
||||
= std::is_trivially_destructible_v<std::expected<T, E>>;
|
||||
|
||||
struct B { ~B(); };
|
||||
|
||||
static_assert( trivially_destructible< int, int > );
|
||||
static_assert( trivially_destructible< B, int > == false );
|
||||
static_assert( trivially_destructible< int, B > == false );
|
||||
static_assert( trivially_destructible< B, B > == false );
|
||||
static_assert( trivially_destructible< void, int > );
|
||||
static_assert( trivially_destructible< void, B > == false );
|
||||
|
||||
enum Result { No, Yes, NoThrow, Trivial };
|
||||
|
||||
// Copy construction
|
||||
|
||||
template<typename T, typename E>
|
||||
constexpr Result copy_constructible
|
||||
= std::is_trivially_copy_constructible_v<std::expected<T, E>> ? Trivial
|
||||
: std::is_copy_constructible_v<std::expected<T, E>> ? Yes
|
||||
: No;
|
||||
|
||||
struct C { C(const C&); };
|
||||
struct D { D(D&&); };
|
||||
|
||||
static_assert( copy_constructible< int, int > == Trivial );
|
||||
static_assert( copy_constructible< C, C > == Yes );
|
||||
static_assert( copy_constructible< C, int > == Yes );
|
||||
static_assert( copy_constructible< int, C > == Yes );
|
||||
static_assert( copy_constructible< int, D > == No );
|
||||
static_assert( copy_constructible< D, int > == No );
|
||||
static_assert( copy_constructible< D, D > == No );
|
||||
static_assert( copy_constructible< void, int > == Trivial );
|
||||
static_assert( copy_constructible< void, C > == Yes );
|
||||
static_assert( copy_constructible< void, D > == No );
|
||||
|
||||
// Move construction
|
||||
|
||||
template<typename T, typename E>
|
||||
constexpr Result move_constructible
|
||||
= std::is_trivially_move_constructible_v<std::expected<T, E>> ? Trivial
|
||||
: std::is_nothrow_move_constructible_v<std::expected<T, E>> ? NoThrow
|
||||
: std::is_move_constructible_v<std::expected<T, E>> ? Yes
|
||||
: No;
|
||||
|
||||
struct E { E(E&&) noexcept; };
|
||||
|
||||
static_assert( move_constructible< int, int > == Trivial );
|
||||
static_assert( move_constructible< C, C > == Yes );
|
||||
static_assert( move_constructible< C, int > == Yes );
|
||||
static_assert( move_constructible< int, C > == Yes );
|
||||
static_assert( move_constructible< D, D > == Yes );
|
||||
static_assert( move_constructible< D, int > == Yes );
|
||||
static_assert( move_constructible< int, D > == Yes );
|
||||
static_assert( move_constructible< E, E > == NoThrow );
|
||||
static_assert( move_constructible< E, int > == NoThrow );
|
||||
static_assert( move_constructible< int, E > == NoThrow );
|
||||
static_assert( move_constructible< void, int > == Trivial );
|
||||
static_assert( move_constructible< void, C > == Yes );
|
||||
static_assert( move_constructible< void, D > == Yes );
|
||||
static_assert( move_constructible< void, E > == NoThrow );
|
||||
|
||||
// Copy assignment
|
||||
|
||||
template<typename T, typename E>
|
||||
constexpr bool copy_assignable
|
||||
= std::is_copy_assignable_v<std::expected<T, E>>;
|
||||
|
||||
struct F { F(F&&); F& operator=(const F&); }; // not copy-constructible
|
||||
struct G { G(const G&); G(G&&); G& operator=(const G&); }; // throwing move
|
||||
|
||||
static_assert( copy_assignable< int, int > );
|
||||
static_assert( copy_assignable< F, int > == false );
|
||||
static_assert( copy_assignable< int, F > == false );
|
||||
static_assert( copy_assignable< F, F > == false );
|
||||
static_assert( copy_assignable< G, int > );
|
||||
static_assert( copy_assignable< int, G > );
|
||||
static_assert( copy_assignable< G, G > == false );
|
||||
static_assert( copy_assignable< void, int > );
|
||||
static_assert( copy_assignable< void, F > == false );
|
||||
static_assert( copy_assignable< void, G > );
|
||||
|
||||
// Move assignment
|
||||
|
||||
template<typename T, typename E>
|
||||
constexpr bool move_assignable
|
||||
= std::is_move_assignable_v<std::expected<T, E>>;
|
||||
|
||||
static_assert( move_assignable< int, int > );
|
||||
static_assert( move_assignable< F, int > );
|
||||
static_assert( move_assignable< int, F > );
|
||||
static_assert( move_assignable< F, F > == false );
|
||||
static_assert( move_assignable< G, int > );
|
||||
static_assert( move_assignable< int, G > );
|
||||
static_assert( move_assignable< G, G > == false );
|
||||
static_assert( move_assignable< void, int > );
|
||||
static_assert( move_assignable< void, F > );
|
||||
static_assert( move_assignable< void, G > );
|
||||
|
||||
// QoI properties
|
||||
static_assert( sizeof(std::expected<char, unsigned char>) == 2 );
|
||||
static_assert( sizeof(std::expected<void, char>) == 2 );
|
||||
static_assert( sizeof(std::expected<void*, char>) == 2 * __alignof(void*) );
|
||||
static_assert( alignof(std::expected<void, char>) == 1 );
|
||||
static_assert( alignof(std::expected<void*, char>) == alignof(void*) );
|
57
libstdc++-v3/testsuite/20_util/expected/swap.cc
Normal file
57
libstdc++-v3/testsuite/20_util/expected/swap.cc
Normal file
@ -0,0 +1,57 @@
|
||||
// { dg-options "-std=gnu++23" }
|
||||
// { dg-do run { target c++23 } }
|
||||
|
||||
#include <expected>
|
||||
#include <testsuite_hooks.h>
|
||||
|
||||
constexpr bool
|
||||
test_swap()
|
||||
{
|
||||
std::expected<int, int> e1(1), e2(2);
|
||||
std::expected<int, int> e3(std::unexpect, 3), e4(std::unexpect, 4);
|
||||
|
||||
swap(e1, e2);
|
||||
VERIFY( e1.value() == 2 );
|
||||
VERIFY( e2.value() == 1 );
|
||||
swap(e1, e3);
|
||||
VERIFY( ! e1.has_value() );
|
||||
VERIFY( e1.error() == 3 );
|
||||
VERIFY( e3.value() == 2 );
|
||||
swap(e1, e3);
|
||||
VERIFY( ! e3.has_value() );
|
||||
VERIFY( e1.value() == 2 );
|
||||
VERIFY( e3.error() == 3 );
|
||||
swap(e3, e4);
|
||||
VERIFY( ! e3.has_value() );
|
||||
VERIFY( ! e4.has_value() );
|
||||
VERIFY( e3.error() == 4 );
|
||||
VERIFY( e4.error() == 3 );
|
||||
|
||||
std::expected<int, int> v1(1), v2(2);
|
||||
std::expected<int, int> v3(std::unexpect, 3), v4(std::unexpect, 4);
|
||||
|
||||
swap(v1, v2);
|
||||
VERIFY( v1.value() == 2 );
|
||||
VERIFY( v2.value() == 1 );
|
||||
swap(v1, v3);
|
||||
VERIFY( ! v1.has_value() );
|
||||
VERIFY( v1.error() == 3 );
|
||||
VERIFY( v3.value() == 2 );
|
||||
swap(v1, v3);
|
||||
VERIFY( ! v3.has_value() );
|
||||
VERIFY( v1.value() == 2 );
|
||||
VERIFY( v3.error() == 3 );
|
||||
swap(v3, v4);
|
||||
VERIFY( ! v3.has_value() );
|
||||
VERIFY( ! v4.has_value() );
|
||||
VERIFY( v3.error() == 4 );
|
||||
VERIFY( v4.error() == 3 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
static_assert( test_swap() );
|
||||
test_swap();
|
||||
}
|
21
libstdc++-v3/testsuite/20_util/expected/synopsis.cc
Normal file
21
libstdc++-v3/testsuite/20_util/expected/synopsis.cc
Normal file
@ -0,0 +1,21 @@
|
||||
// { dg-options "-std=gnu++23" }
|
||||
// { dg-do compile { target c++23 } }
|
||||
|
||||
#include <expected>
|
||||
|
||||
#ifndef __cpp_lib_expected
|
||||
# error "Feature-test macro for expected missing in <expected>"
|
||||
#elif __cpp_lib_expected != 202202L
|
||||
# error "Feature-test macro for expected has wrong value in <expected>"
|
||||
#endif
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<class E> class unexpected;
|
||||
template<class E> class bad_expected_access;
|
||||
template<> class bad_expected_access<void>;
|
||||
struct unexpect_t;
|
||||
extern inline const unexpect_t unexpect;
|
||||
template<class T, class E> class expected;
|
||||
template<class T, class E> requires is_void_v<T> class expected<T, E>;
|
||||
}
|
80
libstdc++-v3/testsuite/20_util/expected/unexpected.cc
Normal file
80
libstdc++-v3/testsuite/20_util/expected/unexpected.cc
Normal file
@ -0,0 +1,80 @@
|
||||
// { dg-options "-std=gnu++23" }
|
||||
// { dg-do run { target c++23 } }
|
||||
|
||||
#include <expected>
|
||||
#include <testsuite_hooks.h>
|
||||
|
||||
static_assert( sizeof(std::unexpected<char>) == 1 );
|
||||
|
||||
constexpr bool
|
||||
test()
|
||||
{
|
||||
std::unexpected<int> u1(1);
|
||||
VERIFY( u1.error() == 1 );
|
||||
|
||||
std::unexpected<int> u2(std::in_place, 2);
|
||||
VERIFY( u2.error() == 2 );
|
||||
|
||||
struct X
|
||||
{
|
||||
constexpr X(int i, int j) : n(i+j) { }
|
||||
constexpr X(std::initializer_list<int> l, void*) : n(l.size()) { }
|
||||
|
||||
constexpr X(const X&) = default;
|
||||
constexpr X(X&& x) :n(x.n) { x.n = -1; }
|
||||
|
||||
constexpr X& operator=(const X&) = default;
|
||||
constexpr X& operator=(X&& x) { n = x.n; x.n = -1; return *this; }
|
||||
|
||||
constexpr bool operator==(const X&) const = default;
|
||||
constexpr bool operator==(int i) const { return n == i; }
|
||||
|
||||
int n;
|
||||
};
|
||||
|
||||
std::unexpected<X> u3(std::in_place, 2, 1);
|
||||
VERIFY( u3.error() == 3 );
|
||||
|
||||
std::unexpected<X> u4(std::in_place, {1,2,3,4}, nullptr);
|
||||
VERIFY( u4.error() == 4 );
|
||||
|
||||
std::unexpected<X> u5(u4);
|
||||
VERIFY( u5.error() == 4 );
|
||||
VERIFY( u4.error() == 4 );
|
||||
|
||||
std::unexpected<X> u6(std::move(u4));
|
||||
VERIFY( u6.error() == 4 );
|
||||
VERIFY( u4.error() == -1 );
|
||||
|
||||
u6 = u3;
|
||||
VERIFY( u6.error() == 3 );
|
||||
VERIFY( u3.error() == 3 );
|
||||
|
||||
u5 = std::move(u3);
|
||||
VERIFY( u5.error() == 3 );
|
||||
VERIFY( u3.error() == -1 );
|
||||
|
||||
u5.swap(u3);
|
||||
VERIFY( u3.error() == 3 );
|
||||
VERIFY( u5.error() == -1 );
|
||||
|
||||
swap(u5, u3);
|
||||
VERIFY( u5.error() == 3 );
|
||||
VERIFY( u3.error() == -1 );
|
||||
|
||||
VERIFY( u1 == u1 );
|
||||
VERIFY( u1 != u2 );
|
||||
VERIFY( u3 == u4 );
|
||||
|
||||
// CTAD
|
||||
std::unexpected u7(1L);
|
||||
static_assert( std::is_same_v<decltype(u7), std::unexpected<long>> );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
static_assert( test() );
|
||||
test();
|
||||
}
|
10
libstdc++-v3/testsuite/20_util/expected/version.cc
Normal file
10
libstdc++-v3/testsuite/20_util/expected/version.cc
Normal file
@ -0,0 +1,10 @@
|
||||
// { dg-options "-std=gnu++23" }
|
||||
// { dg-do preprocess { target c++23 } }
|
||||
|
||||
#include <version>
|
||||
|
||||
#ifndef __cpp_lib_expected
|
||||
# error "Feature-test macro for expected missing in <version>"
|
||||
#elif __cpp_lib_expected != 202202L
|
||||
# error "Feature-test macro for expected has wrong value in <version>"
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user