// { dg-options "-std=gnu++17" } // { dg-do run } // Copyright (C) 2016-2019 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. // You should have received a copy of the GNU General Public License along // with this library; see the file COPYING3. If not see // . #include #include #include #include #include #include using namespace std; struct AlwaysThrow { AlwaysThrow() = default; AlwaysThrow(const AlwaysThrow&) { throw nullptr; } AlwaysThrow(AlwaysThrow&&) { throw nullptr; } AlwaysThrow& operator=(const AlwaysThrow&) { throw nullptr; return *this; } AlwaysThrow& operator=(AlwaysThrow&&) { throw nullptr; return *this; } bool operator<(const AlwaysThrow&) const { VERIFY(false); } bool operator<=(const AlwaysThrow&) const { VERIFY(false); } bool operator==(const AlwaysThrow&) const { VERIFY(false); } bool operator!=(const AlwaysThrow&) const { VERIFY(false); } bool operator>=(const AlwaysThrow&) const { VERIFY(false); } bool operator>(const AlwaysThrow&) const { VERIFY(false); } }; struct DeletedMoves { DeletedMoves() = default; DeletedMoves(const DeletedMoves&) = default; DeletedMoves(DeletedMoves&&) = delete; DeletedMoves& operator=(const DeletedMoves&) = default; DeletedMoves& operator=(DeletedMoves&&) = delete; }; void default_ctor() { variant v; VERIFY(holds_alternative(v)); } void copy_ctor() { variant v("a"); VERIFY(holds_alternative(v)); variant u(v); VERIFY(holds_alternative(u)); VERIFY(get(u) == "a"); } void move_ctor() { variant v("a"); VERIFY(holds_alternative(v)); variant u(std::move(v)); VERIFY(holds_alternative(u)); VERIFY(get(u) == "a"); VERIFY(holds_alternative(v)); variant, DeletedMoves> d{std::in_place_index<0>, {1, 2, 3, 4}}; // DeletedMoves is not move constructible, so this uses copy ctor: variant, DeletedMoves> e(std::move(d)); VERIFY(std::get<0>(d).size() == 4); VERIFY(std::get<0>(e).size() == 4); } void arbitrary_ctor() { variant v("a"); VERIFY(holds_alternative(v)); VERIFY(get<1>(v) == "a"); } struct ThrowingMoveCtorThrowsCopyCtor { ThrowingMoveCtorThrowsCopyCtor() noexcept = default; ThrowingMoveCtorThrowsCopyCtor(ThrowingMoveCtorThrowsCopyCtor&&) {} ThrowingMoveCtorThrowsCopyCtor(ThrowingMoveCtorThrowsCopyCtor const&) { throw 0; } ThrowingMoveCtorThrowsCopyCtor& operator=(ThrowingMoveCtorThrowsCopyCtor&&) noexcept = default; ThrowingMoveCtorThrowsCopyCtor& operator=(ThrowingMoveCtorThrowsCopyCtor const&) noexcept = default; }; void copy_assign() { variant v("a"); VERIFY(holds_alternative(v)); variant u; u = v; VERIFY(holds_alternative(u)); VERIFY(get(u) == "a"); { std::variant v1, v2 = ThrowingMoveCtorThrowsCopyCtor(); bool should_throw = false; try { v1 = v2; } catch(int) { should_throw = true; } VERIFY(should_throw); } } void move_assign() { variant v("a"); VERIFY(holds_alternative(v)); variant u; u = std::move(v); VERIFY(holds_alternative(u)); VERIFY(get(u) == "a"); VERIFY(holds_alternative(v)); variant, DeletedMoves> d{std::in_place_index<0>, {1, 2, 3, 4}}; variant, DeletedMoves> e; // DeletedMoves is not move assignable, so this uses copy assignment: e = std::move(d); VERIFY(std::get<0>(d).size() == 4); VERIFY(std::get<0>(e).size() == 4); } void arbitrary_assign() { variant v; v = "a"; VERIFY(holds_alternative(variant("a"))); VERIFY(get<1>(v) == "a"); } void dtor() { struct A { A(int& called) : called(called) {} ~A() { called++; } int& called; }; { int called = 0; { variant a(in_place_index<1>, called); } VERIFY(called == 1); } { int called = 0; { variant a(in_place_index<0>); } VERIFY(called == 0); } } void in_place_index_ctor() { { variant v(in_place_index<1>, "a"); VERIFY(holds_alternative(v)); VERIFY(get<1>(v) == "a"); } { variant v(in_place_index<1>, {'a', 'b'}); VERIFY(holds_alternative(v)); VERIFY(get<1>(v) == "ab"); } } void in_place_type_ctor() { { variant v(in_place_type, "a"); VERIFY(holds_alternative(v)); VERIFY(get<1>(v) == "a"); } { variant v(in_place_type, {'a', 'b'}); VERIFY(holds_alternative(v)); VERIFY(get<1>(v) == "ab"); } } void emplace() { variant v; v.emplace<0>(1); VERIFY(get<0>(v) == 1); v.emplace("a"); VERIFY(get(v) == "a"); v.emplace<1>({'a', 'b'}); VERIFY(get<1>(v) == "ab"); v.emplace({'a', 'c'}); VERIFY(get(v) == "ac"); { variant v; AlwaysThrow a; try { v.emplace<1>(a); } catch (nullptr_t) { } VERIFY(v.valueless_by_exception()); v.emplace<0>(42); VERIFY(!v.valueless_by_exception()); } { variant v; try { v.emplace<1>(AlwaysThrow{}); } catch (nullptr_t) { } VERIFY(v.valueless_by_exception()); v.emplace<0>(42); VERIFY(!v.valueless_by_exception()); } VERIFY(&v.emplace<0>(1) == &std::get<0>(v)); VERIFY(&v.emplace(1) == &std::get(v)); VERIFY(&v.emplace<1>("a") == &std::get<1>(v)); VERIFY(&v.emplace("a") == &std::get(v)); { variant> v; VERIFY(&v.emplace<0>({1,2,3}) == &std::get<0>(v)); VERIFY(&v.emplace>({1,2,3}) == &std::get>(v)); } { // Ensure no copies of the vector are made, only moves. // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87431#c21 // static_assert(__detail::__variant::_Never_valueless_alt>::value); variant> v; v.emplace<2>(1); v.emplace>(1); v.emplace<0>(0); // To test the emplace(initializer_list, Args&&...) members we // can't use AlwaysThrow because elements in an initialier_list // are always copied. Use throw_allocator instead. using Vector = vector>; // static_assert(__detail::__variant::_Never_valueless_alt::value); variant vv; Vector::allocator_type::set_limit(1); vv.emplace<2>(1, 1); Vector::allocator_type::set_limit(1); vv.emplace(1, 1); Vector::allocator_type::set_limit(1); vv.emplace<0>(0); Vector::allocator_type::set_limit(1); vv.emplace<2>({1, 2, 3}); Vector::allocator_type::set_limit(1); vv.emplace({1, 2, 3, 4}); try { Vector::allocator_type::set_limit(0); vv.emplace<2>(1, 1); VERIFY(false); } catch (const __gnu_cxx::forced_error&) { } VERIFY(vv.valueless_by_exception()); } } void test_get() { VERIFY(get<1>(variant("a")) == "a"); VERIFY(get(variant("a")) == "a"); { bool caught = false; try { get<0>(variant("a")); } catch (const bad_variant_access&) { caught = true; } VERIFY(caught); } { bool caught = false; try { get(variant("a")); } catch (const bad_variant_access&) { caught = true; } VERIFY(caught); } } void test_relational() { VERIFY((variant(2) < variant(3))); VERIFY((variant(3) == variant(3))); VERIFY((variant(3) > variant(2))); VERIFY((variant(3) <= variant(3))); VERIFY((variant(2) <= variant(3))); VERIFY((variant(3) >= variant(3))); VERIFY((variant(3) >= variant(2))); VERIFY((variant(2) != variant(3))); VERIFY((variant(2) < variant("a"))); VERIFY((variant(2) > variant("a"))); { variant v, w; try { AlwaysThrow a; v = a; } catch (nullptr_t) { } VERIFY(v.valueless_by_exception()); VERIFY(v < w); VERIFY(v <= w); VERIFY(!(v == w)); VERIFY(v == v); VERIFY(v != w); VERIFY(w > v); VERIFY(w >= v); } } void test_swap() { variant a("a"), b("b"); a.swap(b); VERIFY(get<1>(a) == "b"); VERIFY(get<1>(b) == "a"); swap(a, b); VERIFY(get<1>(a) == "a"); VERIFY(get<1>(b) == "b"); } void test_visit() { { struct Visitor { int operator()(int, float) { return 0; } int operator()(int, double) { return 1; } int operator()(char, float) { return 2; } int operator()(char, double) { return 3; } int operator()(int, float) const { return 5; } int operator()(int, double) const { return 6; } int operator()(char, float) const { return 7; } int operator()(char, double) const { return 8; } } visitor1; VERIFY(visit(visitor1, variant(1), variant(1.0f)) == 0); VERIFY(visit(visitor1, variant(1), variant(1.0)) == 1); VERIFY(visit(visitor1, variant('a'), variant(1.0f)) == 2); VERIFY(visit(visitor1, variant('a'), variant(1.0)) == 3); const auto& visitor2 = visitor1; VERIFY(visit(visitor2, variant(1), variant(1.0f)) == 5); VERIFY(visit(visitor2, variant(1), variant(1.0)) == 6); VERIFY(visit(visitor2, variant('a'), variant(1.0f)) == 7); VERIFY(visit(visitor2, variant('a'), variant(1.0)) == 8); } { struct Visitor { int operator()(int, float) && { return 0; } int operator()(int, double) && { return 1; } int operator()(char, float) && { return 2; } int operator()(char, double) && { return 3; } }; VERIFY(visit(Visitor{}, variant(1), variant(1.0f)) == 0); VERIFY(visit(Visitor{}, variant(1), variant(1.0)) == 1); VERIFY(visit(Visitor{}, variant('a'), variant(1.0f)) == 2); VERIFY(visit(Visitor{}, variant('a'), variant(1.0)) == 3); } } struct Hashable { Hashable(const char* s) : s(s) { } // Non-trivial special member functions: Hashable(const Hashable&) { } Hashable(Hashable&&) noexcept { } ~Hashable() { } string s; bool operator==(const Hashable& rhs) const noexcept { return s == rhs.s; } }; namespace std { template<> struct hash { size_t operator()(const Hashable& h) const noexcept { return hash()(h.s); } }; } void test_hash() { unordered_set> s; VERIFY(s.emplace(3).second); VERIFY(s.emplace("asdf").second); VERIFY(s.emplace().second); VERIFY(s.size() == 3); VERIFY(!s.emplace(3).second); VERIFY(!s.emplace("asdf").second); VERIFY(!s.emplace().second); VERIFY(s.size() == 3); { struct A { operator Hashable() { throw nullptr; } }; variant v; try { v.emplace<1>(A{}); } catch (nullptr_t) { } VERIFY(v.valueless_by_exception()); VERIFY(s.insert(v).second); VERIFY(s.size() == 4); VERIFY(!s.insert(v).second); } } void test_valueless_by_exception() { { AlwaysThrow a; bool caught = false; try { variant v(a); } catch (nullptr_t) { caught = true; } VERIFY(caught); } { AlwaysThrow a; bool caught = false; try { variant v(a); } catch (nullptr_t) { caught = true; } VERIFY(caught); } { variant v; bool caught = false; try { AlwaysThrow a; v = a; } catch (nullptr_t) { caught = true; } VERIFY(caught); VERIFY(v.valueless_by_exception()); } { variant v; bool caught = false; try { v = AlwaysThrow{}; } catch (nullptr_t) { caught = true; } VERIFY(caught); VERIFY(v.valueless_by_exception()); } } int main() { default_ctor(); copy_ctor(); move_ctor(); arbitrary_ctor(); in_place_index_ctor(); in_place_type_ctor(); copy_assign(); move_assign(); arbitrary_assign(); dtor(); emplace(); test_get(); test_relational(); test_swap(); test_visit(); test_hash(); test_valueless_by_exception(); }