7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2025-01-06 21:11:59 +00:00
kicad/qa/tests/common/test_ki_any.cpp
Seth Hillbrand 0b2d4d4879 Revise Copyright statement to align with TLF
Recommendation is to avoid using the year nomenclature as this
information is already encoded in the git repo.  Avoids needing to
repeatly update.

Also updates AUTHORS.txt from current repo with contributor names
2025-01-01 14:12:04 -08:00

841 lines
20 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Tests taken from GCC:
// Copyright (C) 2014-2024 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
#include <boost/test/unit_test.hpp>
#include <memory>
#include <set>
#include <utility>
#include <type_traits>
#include <ki_any.h>
using ki::any;
using ki::any_cast;
//////////////////////////////////////////
// Exception hierarchy
//////////////////////////////////////////
///
static_assert(std::is_base_of<std::bad_cast, ki::bad_any_cast>::value,
"ki::bad_any_cast must derive from std::bad_cast");
//////////////////////////////////////////
// Type requirements
//////////////////////////////////////////
static_assert(std::is_assignable<any&, int>::value);
static_assert(!std::is_assignable<any&, std::unique_ptr<int>>::value);
static_assert(std::is_constructible<any, int>::value);
static_assert(!std::is_constructible<any, std::unique_ptr<int>>::value);
static_assert(!std::is_assignable<any&, const std::unique_ptr<int>&>::value);
static_assert(!std::is_constructible<any&, const std::unique_ptr<int>&>::value);
static_assert(!std::is_assignable<any&, std::unique_ptr<int>&>::value);
static_assert(!std::is_constructible<any&, std::unique_ptr<int>&>::value);
struct NoDefaultCtor
{
NoDefaultCtor() = delete;
};
static_assert(!std::is_constructible_v<any,
std::in_place_type_t<NoDefaultCtor>>);
static_assert(!std::is_constructible_v<any,
std::in_place_type_t<NoDefaultCtor>&>);
static_assert(!std::is_constructible_v<any,
std::in_place_type_t<NoDefaultCtor>&&>);
static_assert(!std::is_constructible_v<any,
const std::in_place_type_t<NoDefaultCtor>&>);
static_assert(!std::is_constructible_v<any,
const std::in_place_type_t<NoDefaultCtor>&&>);
static_assert( std::is_copy_constructible_v<std::tuple<any>> );
struct A {
A(const A&) = default;
explicit A(any value);
};
static_assert(std::is_copy_constructible_v<A>);
BOOST_AUTO_TEST_SUITE( ki_any )
struct combined {
std::vector<int> v;
std::tuple<int, int> t;
template<class... Args>
combined(std::initializer_list<int> il, Args&&... args)
: v(il), t(std::forward<Args>(args)...)
{
}
};
BOOST_AUTO_TEST_CASE( AnyCast_1 )
{
using std::string;
using std::strcmp;
any x(5); // x holds int
BOOST_CHECK(any_cast<int>(x) == 5); // cast to value
any_cast<int&>(x) = 10; // cast to reference
BOOST_CHECK(any_cast<int>(x) == 10);
x = "Meow"; // x holds const char*
BOOST_CHECK(strcmp(any_cast<const char*>(x), "Meow") == 0);
any_cast<const char*&>(x) = "Harry";
BOOST_CHECK(strcmp(any_cast<const char*>(x), "Harry") == 0);
x = string("Meow"); // x holds string
string s, s2("Jane");
s = std::move(any_cast<string&>(x)); // move from any
BOOST_CHECK(s == "Meow");
any_cast<string&>(x) = std::move(s2); // move to any
BOOST_CHECK(any_cast<const string&>(x) == "Jane");
string cat("Meow");
const any y(cat); // const y holds string
BOOST_CHECK(any_cast<const string&>(y) == cat);
}
BOOST_AUTO_TEST_CASE( AnyCast_2 )
{
using ki::bad_any_cast;
any x(1);
auto p = any_cast<double>(&x);
BOOST_CHECK(p == nullptr);
x = 1.0;
p = any_cast<double>(&x);
BOOST_CHECK(p != nullptr);
x = any();
p = any_cast<double>(&x);
BOOST_CHECK(p == nullptr);
try {
any_cast<double>(x);
BOOST_CHECK(false);
} catch (const bad_any_cast&) {
}
}
static int move_count = 0;
BOOST_AUTO_TEST_CASE( AnyCast_3 )
{
struct MoveEnabled
{
MoveEnabled(MoveEnabled&&)
{
++move_count;
}
MoveEnabled() = default;
MoveEnabled(const MoveEnabled&) = default;
};
MoveEnabled m;
MoveEnabled m2 = any_cast<MoveEnabled>(any(m));
BOOST_CHECK(move_count == 1);
MoveEnabled&& m3 = any_cast<MoveEnabled&&>(any(m));
BOOST_CHECK(move_count == 1);
}
BOOST_AUTO_TEST_CASE( AnyCast_4 )
{
struct ExplicitCopy
{
ExplicitCopy() = default;
explicit ExplicitCopy(const ExplicitCopy&) = default;
};
any x = ExplicitCopy();
ExplicitCopy ec{any_cast<ExplicitCopy>(x)};
ExplicitCopy ec2{any_cast<ExplicitCopy>(std::move(x))};
}
BOOST_AUTO_TEST_CASE( AnyCast_5 )
{
struct noncopyable {
noncopyable(noncopyable const&) = delete;
};
any a;
auto p = any_cast<noncopyable>(&a);
BOOST_CHECK( p == nullptr );
}
BOOST_AUTO_TEST_CASE( AnyCast_6 )
{
// The contained value of a std::any is always an object type,
// but any_cast does not forbid checking for function types.
any a(1);
void (*p1)() = any_cast<void()>(&a);
BOOST_CHECK( p1 == nullptr );
int (*p2)(int) = any_cast<int(int)>(&a);
BOOST_CHECK( p2 == nullptr );
int (*p3)() = any_cast<int()>(&std::as_const(a));
BOOST_CHECK( p3 == nullptr );
try {
any_cast<int(&)()>(a);
BOOST_CHECK( false );
} catch (const ki::bad_any_cast&) {
}
try {
any_cast<int(&)()>(std::move(a));
BOOST_CHECK( false );
} catch (const ki::bad_any_cast&) {
}
try {
any_cast<int(&)()>(std::as_const(a));
BOOST_CHECK( false );
} catch (const ki::bad_any_cast&) {
}
}
BOOST_AUTO_TEST_CASE( AnyCast_7 )
{
int arr[3];
any a(arr);
BOOST_CHECK( a.type() == typeid(int*) ); // contained value is decayed
int (*p1)[3] = any_cast<int[3]>(&a);
BOOST_CHECK( a.type() != typeid(int[3]) ); // so any_cast should return nullptr
BOOST_CHECK( p1 == nullptr );
int (*p2)[] = any_cast<int[]>(&a);
BOOST_CHECK( a.type() != typeid(int[]) ); // so any_cast should return nullptr
BOOST_CHECK( p2 == nullptr );
const int (*p3)[] = any_cast<int[]>(&std::as_const(a));
BOOST_CHECK( p3 == nullptr );
}
struct LocationAware
{
LocationAware() { }
~LocationAware() { BOOST_CHECK(self == this); }
LocationAware(const LocationAware&) { }
LocationAware& operator=(const LocationAware&) { return *this; }
LocationAware(LocationAware&&) noexcept { }
LocationAware& operator=(LocationAware&&) noexcept { return *this; }
void* const self = this;
};
static_assert(std::is_nothrow_move_constructible<LocationAware>::value, "");
static_assert(!std::is_trivially_copyable<LocationAware>::value, "");
BOOST_AUTO_TEST_CASE( NonTrivialType_1 )
{
LocationAware l;
any a = l;
}
BOOST_AUTO_TEST_CASE( NonTrivialType_2 )
{
LocationAware l;
any a = l;
any b = a;
{
any tmp = std::move(a);
a = std::move(b);
b = std::move(tmp);
}
}
BOOST_AUTO_TEST_CASE( NonTrivialType_3 )
{
LocationAware l;
any a = l;
any b = a;
swap(a, b);
}
BOOST_AUTO_TEST_CASE( MakeAny )
{
const int i = 42;
auto o = ki::make_any<int>(i);
int& i2 = any_cast<int&>(o);
BOOST_CHECK( i2 == 42 );
BOOST_CHECK( &i2 != &i );
auto o2 = ki::make_any<std::tuple<int, int>>(1, 2);
std::tuple<int, int>& t = any_cast<std::tuple<int, int>&>(o2);
BOOST_CHECK( std::get<0>(t) == 1 && std::get<1>(t) == 2);
auto o3 = ki::make_any<std::vector<int>>({42, 666});
std::vector<int>& v = any_cast<std::vector<int>&>(o3);
BOOST_CHECK(v[0] == 42 && v[1] == 666);
auto o4 = ki::make_any<combined>({42, 666});
combined& c = any_cast<combined&>(o4);
BOOST_CHECK(c.v[0] == 42 && c.v[1] == 666
&& std::get<0>(c.t) == 0 && std::get<1>(c.t) == 0 );
auto o5 = ki::make_any<combined>({1, 2}, 3, 4);
combined& c2 = any_cast<combined&>(o5);
BOOST_CHECK(c2.v[0] == 1 && c2.v[1] == 2
&& std::get<0>(c2.t) == 3 && std::get<1>(c2.t) == 4 );
}
BOOST_AUTO_TEST_CASE( TypeObserver )
{
any x;
BOOST_CHECK( x.type() == typeid(void) );
x = 1;
BOOST_CHECK( x.type() == typeid(int) );
x = any();
BOOST_CHECK( x.type() == typeid(void) );
}
BOOST_AUTO_TEST_CASE( Swap )
{
any x(1);
any y;
swap(x, y);
BOOST_CHECK( !x.has_value() );
BOOST_CHECK( y.has_value() );
}
BOOST_AUTO_TEST_CASE( Modifiers )
{
any x(1);
any y;
x.swap(y);
BOOST_CHECK( !x.has_value() );
BOOST_CHECK( y.has_value() );
x.swap(y);
BOOST_CHECK( x.has_value() );
BOOST_CHECK( !y.has_value() );
x.reset();
BOOST_CHECK( !x.has_value() );
}
BOOST_AUTO_TEST_CASE( Construction_InPlace_1 )
{
const int i = 42;
any o(std::in_place_type<int>, i);
int& i2 = any_cast<int&>(o);
BOOST_CHECK( i2 == 42 );
BOOST_CHECK( &i2 != &i );
any o2(std::in_place_type<std::tuple<int, int>>, 1, 2);
std::tuple<int, int>& t = any_cast<std::tuple<int, int>&>(o2);
BOOST_CHECK( std::get<0>(t) == 1 && std::get<1>(t) == 2);
any o3(std::in_place_type<std::vector<int>>, {42, 666});
std::vector<int>& v = any_cast<std::vector<int>&>(o3);
BOOST_CHECK(v[0] == 42 && v[1] == 666);
any o4(std::in_place_type<combined>, {42, 666});
combined& c = any_cast<combined&>(o4);
BOOST_CHECK(c.v[0] == 42 && c.v[1] == 666
&& std::get<0>(c.t) == 0 && std::get<1>(c.t) == 0 );
any o5(std::in_place_type<combined>, {1, 2}, 3, 4);
combined& c2 = any_cast<combined&>(o5);
BOOST_CHECK(c2.v[0] == 1 && c2.v[1] == 2
&& std::get<0>(c2.t) == 3 && std::get<1>(c2.t) == 4 );
any o6(std::in_place_type<int&>, i);
BOOST_CHECK(o6.type() == o.type());
any o7(std::in_place_type<void()>, nullptr);
any o8(std::in_place_type<void(*)()>, nullptr);
BOOST_CHECK(o7.type() == o8.type());
any o9(std::in_place_type<char(&)[42]>, nullptr);
any o10(std::in_place_type<char*>, nullptr);
BOOST_CHECK(o9.type() == o10.type());
}
bool moved = false;
bool copied = false;
struct X
{
X() = default;
X(const X&) { copied = true; }
X(X&&) { moved = true; }
};
struct X2
{
X2() = default;
X2(const X2&) { copied = true; }
X2(X2&&) noexcept { moved = true; }
};
BOOST_AUTO_TEST_CASE( Construction_Basic_1 )
{
moved = false;
X x;
any a1(x);
BOOST_CHECK(moved == false);
any a2(std::move(x));
BOOST_CHECK(moved == true);
}
BOOST_AUTO_TEST_CASE( Construction_Basic_2 )
{
moved = false;
X x;
any a1(x);
BOOST_CHECK(moved == false);
copied = false;
any a2(std::move(a1));
BOOST_CHECK(copied == false);
}
BOOST_AUTO_TEST_CASE( Construction_Basic_3 )
{
moved = false;
X2 x;
any a1(x);
BOOST_CHECK(moved == false);
copied = false;
any a2(std::move(a1));
BOOST_CHECK(copied == false);
BOOST_CHECK(moved == true);
}
BOOST_AUTO_TEST_CASE( Construction_Basic_4)
{
any x;
BOOST_CHECK( !x.has_value() );
any y(x);
BOOST_CHECK( !x.has_value() );
BOOST_CHECK( !y.has_value() );
any z(std::move(y));
BOOST_CHECK( !y.has_value() );
BOOST_CHECK( !z.has_value() );
}
BOOST_AUTO_TEST_CASE( Construction_Basic_5)
{
any x(1);
BOOST_CHECK( x.has_value() );
any y(x);
BOOST_CHECK( x.has_value() );
BOOST_CHECK( y.has_value() );
any z(std::move(y));
BOOST_CHECK( !y.has_value() );
BOOST_CHECK( z.has_value() );
}
BOOST_AUTO_TEST_CASE( Construction_InPlace_2 )
{
auto a = any(std::in_place_type<any>, 5);
BOOST_CHECK( any_cast<int>(any_cast<any>(a)) == 5 );
auto b = any(std::in_place_type<any>, {1});
(void) any_cast<std::initializer_list<int>>(any_cast<any>(b));
}
BOOST_AUTO_TEST_CASE( Construction_Pair )
{
any p = std::pair<any, any>(1, 1);
auto pt = any_cast<std::pair<any, any>>(p);
BOOST_CHECK( any_cast<int>(pt.first) == 1 );
BOOST_CHECK( any_cast<int>(pt.second) == 1 );
any t = std::tuple<any>(1);
auto tt = any_cast<std::tuple<any>>(t);
BOOST_CHECK( any_cast<int>(std::get<0>(tt)) == 1 );
}
// Alignment requiremnts of this type prevent it being stored in 'any'
struct alignas(2 * alignof(void*)) X3 { };
bool
stored_internally(void* obj, const ki::any& a)
{
std::uintptr_t a_addr = reinterpret_cast<std::uintptr_t>(&a);
std::uintptr_t a_end = a_addr + sizeof(a);
std::uintptr_t obj_addr = reinterpret_cast<std::uintptr_t>(obj);
return (a_addr <= obj_addr) && (obj_addr < a_end);
}
BOOST_AUTO_TEST_CASE( Construction_Alignment )
{
any a = X3{};
X3& x = any_cast<X3&>(a);
BOOST_CHECK( !stored_internally(&x, a) );
a = 'X';
char& c = any_cast<char&>(a);
BOOST_CHECK( stored_internally(&c, a) );
}
struct wrapper
{
wrapper() = default;
wrapper(const any& t);
wrapper(const wrapper& w);
auto& operator=(const any& t);
auto& operator=(const wrapper& w)
{
value = w.value;
return *this;
}
any value;
};
BOOST_AUTO_TEST_CASE( Construction_Wrapper )
{
wrapper a, b;
a = b;
}
// Following tests commented out until Apple Clang build version >= 16
/*
struct aggressive_aggregate
{
int a;
int b;
};
BOOST_AUTO_TEST_CASE( Construction_Aggregate_1 )
{
any x{std::in_place_type<aggressive_aggregate>, 1, 2};
BOOST_CHECK(any_cast<aggressive_aggregate>(x).a == 1);
BOOST_CHECK(any_cast<aggressive_aggregate>(x).b == 2);
any y{std::in_place_type<aggressive_aggregate>, 1};
BOOST_CHECK(any_cast<aggressive_aggregate>(y).a == 1);
BOOST_CHECK(any_cast<aggressive_aggregate>(y).b == 0);
any z{std::in_place_type<aggressive_aggregate>};
BOOST_CHECK(any_cast<aggressive_aggregate>(z).a == 0);
BOOST_CHECK(any_cast<aggressive_aggregate>(z).b == 0);
}
BOOST_AUTO_TEST_CASE( Construction_Aggregate_2 )
{
any x;
x.emplace<aggressive_aggregate>(1, 2);
BOOST_CHECK(any_cast<aggressive_aggregate>(x).a == 1);
BOOST_CHECK(any_cast<aggressive_aggregate>(x).b == 2);
x.emplace<aggressive_aggregate>(1);
BOOST_CHECK(any_cast<aggressive_aggregate>(x).a == 1);
BOOST_CHECK(any_cast<aggressive_aggregate>(x).b == 0);
x.emplace<aggressive_aggregate>();
BOOST_CHECK(any_cast<aggressive_aggregate>(x).a == 0);
BOOST_CHECK(any_cast<aggressive_aggregate>(x).b == 0);
}
*/
std::set<const void*> live_objects;
struct A {
A() { live_objects.insert(this); }
~A() { live_objects.erase(this); }
A(const A& a) { BOOST_CHECK(live_objects.count(&a)); live_objects.insert(this); }
};
BOOST_AUTO_TEST_CASE( Assign_1 )
{
any a;
a = a;
BOOST_CHECK( !a.has_value() );
a = A{};
a = a;
BOOST_CHECK( a.has_value() );
a.reset();
BOOST_CHECK( live_objects.empty() );
}
BOOST_AUTO_TEST_CASE( Assign_2 )
{
struct X {
any a;
};
X x;
std::swap(x, x); // results in "self-move-assignment" of X::a
BOOST_CHECK( !x.a.has_value() );
x.a = A{};
std::swap(x, x); // results in "self-move-assignment" of X::a
BOOST_CHECK( x.a.has_value() );
x.a.reset();
BOOST_CHECK( live_objects.empty() );
}
BOOST_AUTO_TEST_CASE( Assign_3 )
{
any a;
a.swap(a);
BOOST_CHECK( !a.has_value() );
a = A{};
a.swap(a);
BOOST_CHECK( a.has_value() );
a.reset();
BOOST_CHECK( live_objects.empty() );
}
BOOST_AUTO_TEST_CASE( Assign_4 )
{
moved = false;
X x;
any a1;
a1 = x;
BOOST_CHECK(moved == false);
any a2;
copied = false;
a2 = std::move(x);
BOOST_CHECK(moved == true);
BOOST_CHECK(copied == false);
}
BOOST_AUTO_TEST_CASE( Assign_5 )
{
moved = false;
X x;
any a1;
a1 = x;
BOOST_CHECK(moved == false);
any a2;
copied = false;
a2 = std::move(a1);
BOOST_CHECK(moved == false);
BOOST_CHECK(copied == false);
}
BOOST_AUTO_TEST_CASE( Assign_6 )
{
moved = false;
X2 x;
any a1;
a1 = x;
BOOST_CHECK(copied && moved);
any a2;
moved = false;
copied = false;
a2 = std::move(a1);
BOOST_CHECK(moved == true);
BOOST_CHECK(copied == false);
}
BOOST_AUTO_TEST_CASE( Assign_7 )
{
any x;
any y;
y = x;
BOOST_CHECK( !x.has_value() );
BOOST_CHECK( !y.has_value() );
y = std::move(x);
BOOST_CHECK( !x.has_value() );
BOOST_CHECK( !y.has_value() );
}
BOOST_AUTO_TEST_CASE( Assign_8 )
{
any x(1);
any y;
y = x;
BOOST_CHECK( x.has_value() );
BOOST_CHECK( y.has_value() );
x = std::move(y);
BOOST_CHECK( x.has_value() );
BOOST_CHECK( !y.has_value() );
x = y;
BOOST_CHECK( !x.has_value() );
BOOST_CHECK( !y.has_value() );
}
bool should_throw = false;
struct Bad
{
Bad() = default;
Bad(const Bad&) {if (should_throw) throw 666;}
};
struct Bad2
{
Bad2() = default;
Bad2(const Bad2&) {if (should_throw) throw 666;}
Bad2(Bad2&&) noexcept {}
};
int del_count = 0;
struct Good
{
Good() = default;
Good(const Good&) = default;
Good(Good&&) = default;
~Good() {++del_count;}
};
BOOST_AUTO_TEST_CASE( Exceptions )
{
any a1 = Good();
del_count = 0;
try {
Bad b;
any a2 = b;
should_throw = true;
a1 = a2;
} catch (...) {
auto x = any_cast<Good>(a1);
BOOST_CHECK( del_count == 0 );
BOOST_CHECK( a1.has_value() );
any_cast<Good>(a1);
}
any a3 = Good();
del_count = 0;
try {
Bad2 b;
any a4 = b;
should_throw = true;
a3 = a4;
} catch (...) {
auto x = any_cast<Good>(a1);
BOOST_CHECK( del_count == 0 );
BOOST_CHECK( a1.has_value() );
any_cast<Good>(a1);
}
}
BOOST_AUTO_TEST_CASE( Emplace_1 )
{
const int i = 42;
any o;
o.emplace<int>(i);
int& i2 = any_cast<int&>(o);
BOOST_CHECK( i2 == 42 );
BOOST_CHECK( &i2 != &i );
any o2;
o2.emplace<std::tuple<int, int>>(1, 2);
std::tuple<int, int>& t = any_cast<std::tuple<int, int>&>(o2);
BOOST_CHECK( std::get<0>(t) == 1 && std::get<1>(t) == 2);
any o3;
o3.emplace<std::vector<int>>({42, 666});
std::vector<int>& v = any_cast<std::vector<int>&>(o3);
BOOST_CHECK(v[0] == 42 && v[1] == 666);
any o4;
o4.emplace<combined>({42, 666});
combined& c = any_cast<combined&>(o4);
BOOST_CHECK(c.v[0] == 42 && c.v[1] == 666
&& std::get<0>(c.t) == 0 && std::get<1>(c.t) == 0 );
any o5;
o5.emplace<combined>({1, 2}, 3, 4);
combined& c2 = any_cast<combined&>(o5);
BOOST_CHECK(c2.v[0] == 1 && c2.v[1] == 2
&& std::get<0>(c2.t) == 3 && std::get<1>(c2.t) == 4 );
any o6;
o6.emplace<const int&>(i);
BOOST_CHECK(o6.type() == o.type());
any o7;
o7.emplace<void()>(nullptr);
any o8;
o8.emplace<void(*)()>(nullptr);
BOOST_CHECK(o7.type() == o8.type());
any o9;
o9.emplace<char(&)[42]>(nullptr);
any o10;
o10.emplace<char*>(nullptr);
BOOST_CHECK(o9.type() == o10.type());
any o11;
BOOST_CHECK(&o11.emplace<int>(42) == &any_cast<int&>(o11));
BOOST_CHECK(&o11.emplace<std::vector<int>>({1,2,3}) ==
&any_cast<std::vector<int>&>(o11));
}
struct X_e
{
X_e() = default;
X_e(const X_e&) { copied = true; }
X_e(X_e&&) { moved = true; }
};
struct X2_e
{
X2_e() = default;
X2_e(const X2_e&) { copied = true; }
X2_e(X2_e&&) noexcept { moved = true; }
};
BOOST_AUTO_TEST_SUITE_END()