Add proper type deduction to visit_by_index

This commit is contained in:
Peter Dimov
2021-09-15 01:30:09 +03:00
parent ed4bebff3d
commit 8fd9f830a9
2 changed files with 86 additions and 10 deletions

View File

@ -2213,27 +2213,27 @@ void swap( variant<T...> & v, variant<T...> & w )
namespace detail namespace detail
{ {
template<class V, class... F> struct visit_by_index_L template<class R, class V, class... F> using Vret2 = mp11::mp_eval_if_not< std::is_same<R, deduced>, R, front_if_same, mp11::mp_transform<mp11::mp_invoke_q, mp11::mp_list<Qret<F>...>, apply_cv_ref<V>> >;
template<class R, class V, class... F> struct visit_by_index_L
{ {
V&& v; V&& v;
std::tuple<F&&...> tp; std::tuple<F&&...> tp;
template<class I> void operator()( I ) const template<class I> constexpr detail::Vret2<R, V, F...> operator()( I ) const
{ {
std::get<I::value>( std::move(tp) )( unsafe_get<I::value>( std::forward<V>(v) ) ); return std::get<I::value>( std::move(tp) )( unsafe_get<I::value>( std::forward<V>(v) ) );
} }
}; };
} // namespace detail } // namespace detail
template<class V, class... F> void visit_by_index( V&& v, F&&... f ) template<class R = detail::deduced, class V, class... F> constexpr auto visit_by_index( V&& v, F&&... f ) -> detail::Vret2<R, V, F...>
{ {
constexpr auto N = variant_size<V>::value; static_assert( variant_size<V>::value == sizeof...(F), "Incorrect number of function objects" );
static_assert( N == sizeof...(F), "Incorrect number of function objects" ); return mp11::mp_with_index<variant_size<V>::value>( v.index(),
detail::visit_by_index_L<R, V, F...>{ std::forward<V>(v), std::tuple<F&&...>( std::forward<F>(f)... ) } );
mp11::mp_with_index<N>( v.index(),
detail::visit_by_index_L<V, F...>{ std::forward<V>(v), std::tuple<F&&...>( std::forward<F>(f)... ) } );
} }
// hashing support // hashing support

View File

@ -4,9 +4,32 @@
#include <boost/variant2/variant.hpp> #include <boost/variant2/variant.hpp>
#include <boost/core/lightweight_test.hpp> #include <boost/core/lightweight_test.hpp>
#include <boost/core/lightweight_test_trait.hpp>
#include <boost/mp11.hpp>
#include <boost/config.hpp> #include <boost/config.hpp>
using namespace boost::variant2; using namespace boost::variant2;
using boost::mp11::mp_int;
struct X
{
};
struct F1
{
int operator()( X& ) const { return 1; }
int operator()( X const& ) const { return 2; }
int operator()( X&& ) const { return 3; }
int operator()( X const&& ) const { return 4; }
};
struct F2
{
mp_int<1> operator()( X& ) const { return {}; }
mp_int<2> operator()( X const& ) const { return {}; }
mp_int<3> operator()( X&& ) const { return {}; }
mp_int<4> operator()( X const&& ) const { return {}; }
};
int main() int main()
{ {
@ -20,7 +43,7 @@ int main()
} }
{ {
variant<int const, int, float const> v( 1 ); variant<int const, int, float const> v( in_place_index_t<0>(), 1 );
visit_by_index( v, visit_by_index( v,
[]( int const& x ){ BOOST_TEST_EQ( x, 1 ); }, []( int const& x ){ BOOST_TEST_EQ( x, 1 ); },
@ -46,5 +69,58 @@ int main()
[]( float const& x ){ BOOST_TEST_EQ( x, 3.14f ); } ); []( float const& x ){ BOOST_TEST_EQ( x, 3.14f ); } );
} }
{
variant<int, float> const v( 7 );
auto r = visit_by_index( v,
[]( int const& x ) -> double { return x; },
[]( float const& x ) -> double { return x; } );
BOOST_TEST_TRAIT_SAME( decltype(r), double );
BOOST_TEST_EQ( r, 7.0 );
}
{
variant<int, float> const v( 2.0f );
auto r = visit_by_index( v,
[]( int const& x ) { return x + 0.0; },
[]( float const& x ) { return x + 0.0; } );
BOOST_TEST_TRAIT_SAME( decltype(r), double );
BOOST_TEST_EQ( r, 2.0 );
}
{
variant<int, float, double> const v( 3.0 );
auto r = visit_by_index<double>( v,
[]( int const& x ) { return x; },
[]( float const& x ) { return x; },
[]( double const& x ) { return x; } );
BOOST_TEST_TRAIT_SAME( decltype(r), double );
BOOST_TEST_EQ( r, 3.0 );
}
{
variant<X> v;
variant<X> const cv;
F1 f1;
BOOST_TEST_EQ( visit_by_index( v, f1 ), 1 );
BOOST_TEST_EQ( visit_by_index( cv, f1 ), 2 );
BOOST_TEST_EQ( visit_by_index( std::move( v ), f1 ), 3 );
BOOST_TEST_EQ( visit_by_index( std::move( cv ), f1 ), 4 );
F2 f2;
BOOST_TEST_EQ( visit_by_index<int>( v, f2 ), 1 );
BOOST_TEST_EQ( visit_by_index<int>( cv, f2 ), 2 );
BOOST_TEST_EQ( visit_by_index<int>( std::move( v ), f2 ), 3 );
BOOST_TEST_EQ( visit_by_index<int>( std::move( cv ), f2 ), 4 );
}
return boost::report_errors(); return boost::report_errors();
} }