Index: libcxx/docs/Cxx2aStatusPaperStatus.csv =================================================================== --- libcxx/docs/Cxx2aStatusPaperStatus.csv +++ libcxx/docs/Cxx2aStatusPaperStatus.csv @@ -59,7 +59,7 @@ "`P0595R2 `__","CWG","P0595R2 std::is_constant_evaluated()","San Diego","|Complete|","9.0" "`P0602R4 `__","LWG","variant and optional should propagate copy/move triviality","San Diego","|Complete|","8.0" "`P0608R3 `__","LWG","A sane variant converting constructor","San Diego","|Complete|","9.0" -"`P0655R1 `__","LWG","visit: Explicit Return Type for visit","San Diego","* *","" +"`P0655R1 `__","LWG","visit: Explicit Return Type for visit","San Diego","|Complete|","12.0" "`P0771R1 `__","LWG","std::function move constructor should be noexcept","San Diego","|Complete|","6.0" "`P0896R4 `__","LWG","The One Ranges Proposal","San Diego","* *","" "`P0899R1 `__","LWG","P0899R1 - LWG 3016 is not a defect","San Diego","|Nothing To Do|","" Index: libcxx/include/variant =================================================================== --- libcxx/include/variant +++ libcxx/include/variant @@ -169,6 +169,9 @@ template constexpr see below visit(Visitor&&, Variants&&...); + template + constexpr R visit(Visitor&&, Variants&&...); + // 20.7.7, class monostate struct monostate; @@ -583,6 +586,16 @@ __make_value_visitor(_VSTD::forward<_Visitor>(__visitor)), _VSTD::forward<_Vs>(__vs)...); } +#if _LIBCPP_STD_VER > 17 + template + inline _LIBCPP_INLINE_VISIBILITY + static constexpr _Rp __visit_value(_Visitor&& __visitor, + _Vs&&... __vs) { + return __visit_alt( + __make_value_visitor<_Rp>(_VSTD::forward<_Visitor>(__visitor)), + _VSTD::forward<_Vs>(__vs)...); + } +#endif private: template @@ -605,12 +618,43 @@ _Visitor&& __visitor; }; +#if _LIBCPP_STD_VER > 17 + template + struct __value_visitor_return_type { + template + inline _LIBCPP_INLINE_VISIBILITY + constexpr _Rp operator()(_Alts&&... __alts) const { + __std_visit_exhaustive_visitor_check< + _Visitor, + decltype((_VSTD::forward<_Alts>(__alts).__value))...>(); + if constexpr (is_void_v<_Rp>) { + _VSTD::__invoke_constexpr(_VSTD::forward<_Visitor>(__visitor), + _VSTD::forward<_Alts>(__alts).__value...); + } + else { + return _VSTD::__invoke_constexpr(_VSTD::forward<_Visitor>(__visitor), + _VSTD::forward<_Alts>(__alts).__value...); + } + } + + _Visitor&& __visitor; + }; +#endif + template inline _LIBCPP_INLINE_VISIBILITY static constexpr auto __make_value_visitor(_Visitor&& __visitor) { return __value_visitor<_Visitor>{_VSTD::forward<_Visitor>(__visitor)}; } + +#if _LIBCPP_STD_VER > 17 + template + inline _LIBCPP_INLINE_VISIBILITY + static constexpr auto __make_value_visitor(_Visitor&& __visitor) { + return __value_visitor_return_type<_Rp, _Visitor>{_VSTD::forward<_Visitor>(__visitor)}; + } }; +#endif } // namespace __visitation @@ -1594,18 +1638,37 @@ template inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS -constexpr decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) { - using __variant_detail::__visitation::__variant; - bool __results[] = {__vs.valueless_by_exception()...}; - for (bool __result : __results) { - if (__result) { +constexpr void __throw_if_valueless(_Visitor&& __visitor, _Vs&&... __vs) { + const bool __valueless = (... || __vs.valueless_by_exception()); + if (__valueless) { __throw_bad_variant_access(); - } } +} + +template +inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS +constexpr decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) { + using __variant_detail::__visitation::__variant; + _VSTD::__throw_if_valueless(_VSTD::forward<_Visitor>(__visitor), + _VSTD::forward<_Vs>(__vs)...); return __variant::__visit_value(_VSTD::forward<_Visitor>(__visitor), _VSTD::forward<_Vs>(__vs)...); } +#if _LIBCPP_STD_VER > 17 +template +inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS +constexpr _Rp visit(_Visitor&& __visitor, _Vs&&... __vs) { + using __variant_detail::__visitation::__variant; + _VSTD::__throw_if_valueless(_VSTD::forward<_Visitor>(__visitor), + _VSTD::forward<_Vs>(__vs)...); + return __variant::__visit_value<_Rp>(_VSTD::forward<_Visitor>(__visitor), + _VSTD::forward<_Vs>(__vs)...); +} +#endif + struct _LIBCPP_TEMPLATE_VIS monostate {}; inline _LIBCPP_INLINE_VISIBILITY Index: libcxx/test/std/utilities/variant/variant.visit/robust_against_adl.pass.cpp =================================================================== --- libcxx/test/std/utilities/variant/variant.visit/robust_against_adl.pass.cpp +++ libcxx/test/std/utilities/variant/variant.visit/robust_against_adl.pass.cpp @@ -32,6 +32,10 @@ std::variant*, int> v = nullptr; std::visit([](auto){}, v); std::visit([](auto) -> Holder* { return nullptr; }, v); +#if TEST_STD_VER > 17 + std::visit([](auto){}, v); + std::visit([](auto) -> Holder* { return nullptr; }, v); +#endif } return true; } Index: libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp =================================================================== --- libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp +++ libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp @@ -27,70 +27,8 @@ #include #include "test_macros.h" -#include "type_id.h" #include "variant_test_helpers.h" -enum CallType : unsigned { - CT_None, - CT_NonConst = 1, - CT_Const = 2, - CT_LValue = 4, - CT_RValue = 8 -}; - -inline constexpr CallType operator|(CallType LHS, CallType RHS) { - return static_cast(static_cast(LHS) | - static_cast(RHS)); -} - -struct ForwardingCallObject { - - template - ForwardingCallObject& operator()(Args&&...) & { - set_call(CT_NonConst | CT_LValue); - return *this; - } - - template - const ForwardingCallObject& operator()(Args&&...) const & { - set_call(CT_Const | CT_LValue); - return *this; - } - - template - ForwardingCallObject&& operator()(Args&&...) && { - set_call(CT_NonConst | CT_RValue); - return std::move(*this); - } - - template - const ForwardingCallObject&& operator()(Args&&...) const && { - set_call(CT_Const | CT_RValue); - return std::move(*this); - } - - template static void set_call(CallType type) { - assert(last_call_type == CT_None); - assert(last_call_args == nullptr); - last_call_type = type; - last_call_args = std::addressof(makeArgumentID()); - } - - template static bool check_call(CallType type) { - bool result = last_call_type == type && last_call_args && - *last_call_args == makeArgumentID(); - last_call_type = CT_None; - last_call_args = nullptr; - return result; - } - - static CallType last_call_type; - static const TypeID *last_call_args; -}; - -CallType ForwardingCallObject::last_call_type = CT_None; -const TypeID *ForwardingCallObject::last_call_args = nullptr; - void test_call_operator_forwarding() { using Fn = ForwardingCallObject; Fn obj{}; @@ -296,18 +234,6 @@ } } -struct ReturnFirst { - template constexpr int operator()(int f, Args &&...) const { - return f; - } -}; - -struct ReturnArity { - template constexpr int operator()(Args &&...) const { - return sizeof...(Args); - } -}; - void test_constexpr() { constexpr ReturnFirst obj{}; constexpr ReturnArity aobj{}; Index: libcxx/test/std/utilities/variant/variant.visit/visit_return_type.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/utilities/variant/variant.visit/visit_return_type.pass.cpp @@ -0,0 +1,430 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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, c++17 + +// Throwing bad_variant_access is supported starting in macosx10.13 +// XFAIL: with_system_cxx_lib=macosx10.12 && !no-exceptions +// XFAIL: with_system_cxx_lib=macosx10.11 && !no-exceptions +// XFAIL: with_system_cxx_lib=macosx10.10 && !no-exceptions +// XFAIL: with_system_cxx_lib=macosx10.9 && !no-exceptions + +// +// template +// constexpr R visit(Visitor&& vis, Variants&&... vars); + +#include +#include +#include +#include +#include +#include + +#include "test_macros.h" +#include "variant_test_helpers.h" + +template +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))); + } +} + +template +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))); + } +} + +template +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(obj)), ReturnType>); + static_assert(std::is_same_v(cobj)), ReturnType>); + static_assert(std::is_same_v(std::move(obj))), ReturnType>); + static_assert(std::is_same_v(std::move(cobj))), ReturnType>); + } + { // test call operator forwarding - single variant, single arg + using V = std::variant; + V v(42); + static_assert(std::is_same_v(obj, v)), ReturnType>); + static_assert(std::is_same_v(cobj, v)), ReturnType>); + static_assert(std::is_same_v(std::move(obj), v)), ReturnType>); + static_assert(std::is_same_v(std::move(cobj), v)), ReturnType>); + } + { // test call operator forwarding - single variant, multi arg + using V = std::variant; + V v(42l); + static_assert(std::is_same_v(obj, v)), ReturnType>); + static_assert(std::is_same_v(cobj, v)), ReturnType>); + static_assert(std::is_same_v(std::move(obj), v)), ReturnType>); + static_assert(std::is_same_v(std::move(cobj), v)), ReturnType>); + } + { // 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(obj, v, v2)), ReturnType>); + static_assert(std::is_same_v(cobj, v, v2)), ReturnType>); + static_assert(std::is_same_v(std::move(obj), v, v2)), ReturnType>); + static_assert(std::is_same_v(std::move(cobj), v, v2)), ReturnType>); + } + { + using V = std::variant; + V v1(42l), v2("hello"), v3(101), v4(1.1); + static_assert(std::is_same_v(obj, v1, v2, v3, v4)), ReturnType>); + static_assert(std::is_same_v(cobj, v1, v2, v3, v4)), ReturnType>); + static_assert(std::is_same_v(std::move(obj), v1, v2, v3, v4)), ReturnType>); + static_assert(std::is_same_v(std::move(cobj), v1, v2, v3, v4)), ReturnType>); + } + { + using V = std::variant; + V v1(42l), v2("hello"), v3(nullptr), v4(1.1); + static_assert(std::is_same_v(obj, v1, v2, v3, v4)), ReturnType>); + static_assert(std::is_same_v(cobj, v1, v2, v3, v4)), ReturnType>); + static_assert(std::is_same_v(std::move(obj), v1, v2, v3, v4)), ReturnType>); + static_assert(std::is_same_v(std::move(cobj), v1, v2, v3, v4)), ReturnType>); + } +} + +void test_constexpr_void() { + constexpr ReturnFirst obj{}; + constexpr ReturnArity aobj{}; + { + using V = std::variant; + constexpr V v(42); + static_assert((std::visit(obj, v), 42) == 42, ""); + } + { + using V = std::variant; + constexpr V v(42l); + static_assert((std::visit(obj, v), 42) == 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) == 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) == 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) == 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) == 4, ""); + } +} + +void test_constexpr_int() { + 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, ""); + } +} + +template +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://bugs.llvm.org/show_bug.cgi?id=31916 +template +void test_caller_accepts_nonconst() { + struct A {}; + struct Visitor { + auto operator()(A&) { + if constexpr (!std::is_void_v) + { + return ReturnType{}; + } + } + }; + std::variant v; + std::visit(Visitor{}, v); +} + +void test_constexpr_explicit_side_effect() { + auto test_lambda = [](int arg) constexpr { + std::variant v = 101; + std::visit([arg](int& x) constexpr { x = arg; }, v); + return std::get(v); + }; + + static_assert(test_lambda(202) == 202, ""); +} + +int main(int, char**) { + test_call_operator_forwarding(); + test_argument_forwarding(); + test_return_type(); + test_constexpr_void(); + test_exceptions(); + test_caller_accepts_nonconst(); + test_call_operator_forwarding(); + test_argument_forwarding(); + test_return_type(); + test_constexpr_int(); + test_exceptions(); + test_caller_accepts_nonconst(); + test_constexpr_explicit_side_effect(); + + return 0; +} Index: libcxx/test/support/variant_test_helpers.h =================================================================== --- libcxx/test/support/variant_test_helpers.h +++ libcxx/test/support/variant_test_helpers.h @@ -12,8 +12,10 @@ #include #include #include +#include #include "test_macros.h" +#include "type_id.h" #if TEST_STD_VER <= 14 #error This file requires C++17 @@ -85,5 +87,83 @@ } #endif // TEST_HAS_NO_EXCEPTIONS +enum CallType : unsigned { + CT_None, + CT_NonConst = 1, + CT_Const = 2, + CT_LValue = 4, + CT_RValue = 8 +}; + +inline constexpr CallType operator|(CallType LHS, CallType RHS) { + return static_cast(static_cast(LHS) | + static_cast(RHS)); +} + +struct ForwardingCallObject { + + template + ForwardingCallObject& operator()(Args&&...) & { + set_call(CT_NonConst | CT_LValue); + return *this; + } + + template + const ForwardingCallObject& operator()(Args&&...) const & { + set_call(CT_Const | CT_LValue); + return *this; + } + + template + ForwardingCallObject&& operator()(Args&&...) && { + set_call(CT_NonConst | CT_RValue); + return std::move(*this); + } + + template + const ForwardingCallObject&& operator()(Args&&...) const && { + set_call(CT_Const | CT_RValue); + return std::move(*this); + } + + template static void set_call(CallType type) { + assert(last_call_type == CT_None); + assert(last_call_args == nullptr); + last_call_type = type; + last_call_args = std::addressof(makeArgumentID()); + } + + template static bool check_call(CallType type) { + bool result = last_call_type == type && last_call_args && + *last_call_args == makeArgumentID(); + last_call_type = CT_None; + last_call_args = nullptr; + return result; + } + + // To check explicit return type for visit + constexpr operator int() const + { + return 0; + } + + static CallType last_call_type; + static const TypeID *last_call_args; +}; + +CallType ForwardingCallObject::last_call_type = CT_None; +const TypeID *ForwardingCallObject::last_call_args = nullptr; + +struct ReturnFirst { + template constexpr int operator()(int f, Args &&...) const { + return f; + } +}; + +struct ReturnArity { + template constexpr int operator()(Args &&...) const { + return sizeof...(Args); + } +}; #endif // SUPPORT_VARIANT_TEST_HELPERS_H