//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++03, c++11, c++14 // // template // constexpr see below visit(Visitor&& vis, Variants&&... vars); #include #include #include #include #include #include #include "test_macros.h" #include "variant_test_helpers.h" void test_call_operator_forwarding() { using Fn = ForwardingCallObject; Fn obj{}; const Fn &cobj = obj; { // test call operator forwarding - no variant std::visit(obj); assert(Fn::check_call<>(CT_NonConst | CT_LValue)); std::visit(cobj); assert(Fn::check_call<>(CT_Const | CT_LValue)); std::visit(std::move(obj)); assert(Fn::check_call<>(CT_NonConst | CT_RValue)); std::visit(std::move(cobj)); assert(Fn::check_call<>(CT_Const | CT_RValue)); } { // test call operator forwarding - single variant, single arg using V = std::variant; V v(42); std::visit(obj, v); assert(Fn::check_call(CT_NonConst | CT_LValue)); std::visit(cobj, v); assert(Fn::check_call(CT_Const | CT_LValue)); std::visit(std::move(obj), v); assert(Fn::check_call(CT_NonConst | CT_RValue)); std::visit(std::move(cobj), v); assert(Fn::check_call(CT_Const | CT_RValue)); } { // test call operator forwarding - single variant, multi arg using V = std::variant; V v(42l); std::visit(obj, v); assert(Fn::check_call(CT_NonConst | CT_LValue)); std::visit(cobj, v); assert(Fn::check_call(CT_Const | CT_LValue)); std::visit(std::move(obj), v); assert(Fn::check_call(CT_NonConst | CT_RValue)); std::visit(std::move(cobj), v); assert(Fn::check_call(CT_Const | CT_RValue)); } { // test call operator forwarding - multi variant, multi arg using V = std::variant; using V2 = std::variant; V v(42l); V2 v2("hello"); std::visit(obj, v, v2); assert((Fn::check_call(CT_NonConst | CT_LValue))); std::visit(cobj, v, v2); assert((Fn::check_call(CT_Const | CT_LValue))); std::visit(std::move(obj), v, v2); assert((Fn::check_call(CT_NonConst | CT_RValue))); std::visit(std::move(cobj), v, v2); assert((Fn::check_call(CT_Const | CT_RValue))); } { using V = std::variant; V v1(42l), v2("hello"), v3(101), v4(1.1); std::visit(obj, v1, v2, v3, v4); assert((Fn::check_call(CT_NonConst | CT_LValue))); std::visit(cobj, v1, v2, v3, v4); assert((Fn::check_call(CT_Const | CT_LValue))); std::visit(std::move(obj), v1, v2, v3, v4); assert((Fn::check_call(CT_NonConst | CT_RValue))); std::visit(std::move(cobj), v1, v2, v3, v4); assert((Fn::check_call(CT_Const | CT_RValue))); } { using V = std::variant; V v1(42l), v2("hello"), v3(nullptr), v4(1.1); std::visit(obj, v1, v2, v3, v4); assert((Fn::check_call(CT_NonConst | CT_LValue))); std::visit(cobj, v1, v2, v3, v4); assert((Fn::check_call(CT_Const | CT_LValue))); std::visit(std::move(obj), v1, v2, v3, v4); assert((Fn::check_call(CT_NonConst | CT_RValue))); std::visit(std::move(cobj), v1, v2, v3, v4); assert((Fn::check_call(CT_Const | CT_RValue))); } } void test_argument_forwarding() { using Fn = ForwardingCallObject; Fn obj{}; const auto Val = CT_LValue | CT_NonConst; { // single argument - value type using V = std::variant; V v(42); const V &cv = v; std::visit(obj, v); assert(Fn::check_call(Val)); std::visit(obj, cv); assert(Fn::check_call(Val)); std::visit(obj, std::move(v)); assert(Fn::check_call(Val)); std::visit(obj, std::move(cv)); assert(Fn::check_call(Val)); } #if !defined(TEST_VARIANT_HAS_NO_REFERENCES) { // single argument - lvalue reference using V = std::variant; int x = 42; V v(x); const V &cv = v; std::visit(obj, v); assert(Fn::check_call(Val)); std::visit(obj, cv); assert(Fn::check_call(Val)); std::visit(obj, std::move(v)); assert(Fn::check_call(Val)); std::visit(obj, std::move(cv)); assert(Fn::check_call(Val)); } { // single argument - rvalue reference using V = std::variant; int x = 42; V v(std::move(x)); const V &cv = v; std::visit(obj, v); assert(Fn::check_call(Val)); std::visit(obj, cv); assert(Fn::check_call(Val)); std::visit(obj, std::move(v)); assert(Fn::check_call(Val)); std::visit(obj, std::move(cv)); assert(Fn::check_call(Val)); } #endif { // multi argument - multi variant using V = std::variant; V v1(42), v2("hello"), v3(43l); std::visit(obj, v1, v2, v3); assert((Fn::check_call(Val))); std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3)); assert((Fn::check_call(Val))); } { using V = std::variant; V v1(42l), v2("hello"), v3(101), v4(1.1); std::visit(obj, v1, v2, v3, v4); assert((Fn::check_call(Val))); std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3), std::move(v4)); assert((Fn::check_call(Val))); } { using V = std::variant; V v1(42l), v2("hello"), v3(nullptr), v4(1.1); std::visit(obj, v1, v2, v3, v4); assert((Fn::check_call(Val))); std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3), std::move(v4)); assert((Fn::check_call(Val))); } } void test_return_type() { using Fn = ForwardingCallObject; Fn obj{}; const Fn &cobj = obj; { // test call operator forwarding - no variant static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); } { // test call operator forwarding - single variant, single arg using V = std::variant; V v(42); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); } { // test call operator forwarding - single variant, multi arg using V = std::variant; V v(42l); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); } { // test call operator forwarding - multi variant, multi arg using V = std::variant; using V2 = std::variant; V v(42l); V2 v2("hello"); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); } { using V = std::variant; V v1(42l), v2("hello"), v3(101), v4(1.1); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); } { using V = std::variant; V v1(42l), v2("hello"), v3(nullptr), v4(1.1); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); } } void test_constexpr() { constexpr ReturnFirst obj{}; constexpr ReturnArity aobj{}; { using V = std::variant; constexpr V v(42); static_assert(std::visit(obj, v) == 42, ""); } { using V = std::variant; constexpr V v(42l); static_assert(std::visit(obj, v) == 42, ""); } { using V1 = std::variant; using V2 = std::variant; using V3 = std::variant; constexpr V1 v1; constexpr V2 v2(nullptr); constexpr V3 v3; static_assert(std::visit(aobj, v1, v2, v3) == 3, ""); } { using V1 = std::variant; using V2 = std::variant; using V3 = std::variant; constexpr V1 v1; constexpr V2 v2(nullptr); constexpr V3 v3; static_assert(std::visit(aobj, v1, v2, v3) == 3, ""); } { using V = std::variant; constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1); static_assert(std::visit(aobj, v1, v2, v3, v4) == 4, ""); } { using V = std::variant; constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1); static_assert(std::visit(aobj, v1, v2, v3, v4) == 4, ""); } } void test_exceptions() { #ifndef TEST_HAS_NO_EXCEPTIONS ReturnArity obj{}; auto test = [&](auto &&... args) { try { std::visit(obj, args...); } catch (const std::bad_variant_access &) { return true; } catch (...) { } return false; }; { using V = std::variant; V v; makeEmpty(v); assert(test(v)); } { using V = std::variant; using V2 = std::variant; V v; makeEmpty(v); V2 v2("hello"); assert(test(v, v2)); } { using V = std::variant; using V2 = std::variant; V v; makeEmpty(v); V2 v2("hello"); assert(test(v2, v)); } { using V = std::variant; using V2 = std::variant; V v; makeEmpty(v); V2 v2; makeEmpty(v2); assert(test(v, v2)); } { using V = std::variant; V v1(42l), v2(101), v3(202), v4(1.1); makeEmpty(v1); assert(test(v1, v2, v3, v4)); } { using V = std::variant; V v1(42l), v2(101), v3(202), v4(1.1); makeEmpty(v1); makeEmpty(v2); makeEmpty(v3); makeEmpty(v4); assert(test(v1, v2, v3, v4)); } #endif } // See https://llvm.org/PR31916 void test_caller_accepts_nonconst() { struct A {}; struct Visitor { void operator()(A&) {} }; std::variant v; std::visit(Visitor{}, v); } struct MyVariant : std::variant {}; namespace std { template void get(const MyVariant&) { assert(false); } } // namespace std void test_derived_from_variant() { auto v1 = MyVariant{42}; const auto cv1 = MyVariant{142}; std::visit([](auto x) { assert(x == 42); }, v1); std::visit([](auto x) { assert(x == 142); }, cv1); std::visit([](auto x) { assert(x == -1.25f); }, MyVariant{-1.25f}); std::visit([](auto x) { assert(x == 42); }, std::move(v1)); std::visit([](auto x) { assert(x == 142); }, std::move(cv1)); // Check that visit does not take index nor valueless_by_exception members from the base class. struct EvilVariantBase { int index; char valueless_by_exception; }; struct EvilVariant1 : std::variant, std::tuple, EvilVariantBase { using std::variant::variant; }; std::visit([](auto x) { assert(x == 12); }, EvilVariant1{12}); std::visit([](auto x) { assert(x == 12.3); }, EvilVariant1{12.3}); // Check that visit unambiguously picks the variant, even if the other base has __impl member. struct ImplVariantBase { struct Callable { bool operator()() const { assert(false); return false; } }; Callable __impl; }; struct EvilVariant2 : std::variant, ImplVariantBase { using std::variant::variant; }; std::visit([](auto x) { assert(x == 12); }, EvilVariant2{12}); std::visit([](auto x) { assert(x == 12.3); }, EvilVariant2{12.3}); } struct any_visitor { template void operator()(const T&) const {} }; template (), std::declval()))> constexpr bool has_visit(int) { return true; } template constexpr bool has_visit(...) { return false; } void test_sfinae() { struct BadVariant : std::variant, std::variant {}; struct BadVariant2 : private std::variant {}; struct GoodVariant : std::variant {}; struct GoodVariant2 : GoodVariant {}; static_assert(!has_visit(0)); static_assert(!has_visit(0)); static_assert(!has_visit(0)); static_assert(has_visit>(0)); static_assert(has_visit(0)); static_assert(has_visit(0)); } int main(int, char**) { test_call_operator_forwarding(); test_argument_forwarding(); test_return_type(); test_constexpr(); test_exceptions(); test_caller_accepts_nonconst(); test_derived_from_variant(); test_sfinae(); return 0; }