c++ - tutorial - La mejor forma de hacer visitas variadas con lambdas
java 8 tutorial español (1)
Puede usar plantillas variadic para tomar las lambdas y construir una variante de visitante usando herencia. Eso retendría las comprobaciones de tiempo de compilación.
template <typename ReturnType, typename... Lambdas>
struct lambda_visitor : public static_visitor<ReturnType>, public Lambdas... {
lambda_visitor(Lambdas... lambdas) : Lambdas(lambdas)... {}
};
Y una pequeña función auxiliar para usar la deducción del tipo de argumento (requerida para lambdas):
template <typename ReturnType, typename... Lambdas>
lambda_visitor<ReturnType, Lambdas...> make_lambda_visitor(Lambdas... lambdas) {
return { lambdas... };
// you can use the following instead if your compiler doesn''t
// support list-initialization yet
// return lambda_visitor<ReturnType, Lambdas...>(lambdas...);
}
Ahora puedes hacer que los visitantes como este:
auto visitor = make_lambda_visitor<int>([](int) { return 42; },
[](std::string) { return 17; },
[](std::vector<int>) { return 23; });
Nota: debido a un detalle del proceso de resolución de sobrecarga del que no tenía conocimiento, esta elegante solución causa extraños errores de ambigüedad :(
Vea la pregunta de seguimiento para la solución.
Quiero visitar en línea los tipos de variantes con lambdas. Por el momento tengo el siguiente código:
struct Foo {
boost::variant< boost::blank , int , string , vector< int > > var;
template <typename T, typename IL , typename SL , typename VL>
void ApplyOptionals( T& ref, IL&& intOption , SL&& stringOption , VL&& vectorOption ) {
if (var.which() == 1) {
intOption( ref , boost::get< int >(var) );
} else if (var.which() ==2) {
stringOption( ref , boost::get< string>(var) );
} else if (var.which() == 3) {
vectorOption( ref , boost::get< vector< int > >(var) );
}
};
};
// ...
myFooV.ApplyOptionals(
obj,
[](Obj& o, int v) -> void { cout << "int: " << v << endl; o.value = v; },
[](Obj& o, string v) -> void{ cout << "string: " << v << endl; o.name = v; },
[](Obj& o, vector< int >& v) -> void{ v.push_back(257); cout << " vector.. has elements: " << v.size() << endl; o.ids = v; }
);
Sin embargo, el principal inconveniente de este enfoque es que depende del orden de los parámetros de tipo de variante y no detecta en tiempo de compilación no boost::static_visitor
tipos como boost::static_visitor
haría
¿Puedo obtener lo mejor de ambos enfoques?
Trabajando en la excelente respuesta de RMartinho, estoy tratando de resolver este error, parece que la variante piensa que las llamadas del operador () son ambiguas (estoy usando g ++ 4.5.1, es como si no pudiera ver los operadores lambda .
Ver esta solicitud de pregunta para el miembro `... ''es ambigua en g ++ , parece que a c ++ no le gusta la herencia múltiple como una forma de proporcionar múltiples sobrecargas (incluso si las llamadas son completamente no ambiguas debido a la firma diferente)
#include <iostream>
#include <string>
#include <vector>
#include <boost/variant.hpp>
using namespace std;
typedef boost::variant< boost::blank , int , string , vector< int > > var_t;
template <typename ReturnType, typename... Lambdas>
struct lambda_visitor : public boost::static_visitor<ReturnType>, public Lambdas... {
lambda_visitor(Lambdas... lambdas) : Lambdas(lambdas)... { }
};
template <typename ReturnType>
struct lambda_visitor<ReturnType> : public boost::static_visitor<ReturnType> {
lambda_visitor() {}
};
template <typename ReturnType, typename... Lambdas>
lambda_visitor<ReturnType, Lambdas...> make_lambda_visitor(Lambdas... lambdas) {
return { lambdas... };
// you can use the following instead if your compiler doesn''t
// support list-initialization yet
// return lambda_visitor<ReturnType, Lambdas...>(lambdas...);
}
int main() {
vector< int > vit;
vit.push_back(7);
var_t myFooV = vit;
auto visitor = make_lambda_visitor<void>(
[](int v) -> void { cout << "int: " << v << endl; },
[](string& v) -> void{ cout << "string: " << v << endl; },
[](vector< int >& v) -> void{ v.push_back(27); boost::get< vector< int > >(myFooV).push_back(34); cout << " vector.. has elements: " << v.size() << endl; }
);
cout << " and for the grand finale.. " << endl;
boost::apply_visitor( visitor , myFooV );
};
Esto me da aproximadamente un montón de errores de impulso de plantilla, pero la parte distintiva es:
boost_1_46_0/boost/variant/variant.hpp:832:32: error: request for member ‘operator()’ is ambiguous
test2.cpp:44:54: error: candidates are: main()::<lambda(std::vector<int>&)>
test2.cpp:43:47: error: main()::<lambda(std::string&)>
test2.cpp:42:55: error: main()::<lambda(int)>
boost_1_46_0/boost/variant/variant.hpp:832:32: error: return-statement with a value, in function returning ''void''
Este es todo el error, solo en caso de que me falta alguna otra información relevante:
boost_1_46_0/boost/variant/variant.hpp: In member function ‘boost::detail::variant::invoke_visitor<Visitor>::result_type boost::detail::variant::invoke_visitor<Visitor>::internal_visit(T&, int) [with T = std::vector<int>, Visitor = lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> >, boost::detail::variant::invoke_visitor<Visitor>::result_type = void]’:
boost_1_46_0/boost/variant/detail/visitation_impl.hpp:130:9: instantiated from ‘typename Visitor::result_type boost::detail::variant::visitation_impl_invoke_impl(int, Visitor&, VoidPtrCV, T*, mpl_::true_) [with Visitor = boost::detail::variant::invoke_visitor<lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> > >, VoidPtrCV = void*, T = std::vector<int>, typename Visitor::result_type = void, mpl_::true_ = mpl_::bool_<true>]’
boost_1_46_0/boost/variant/detail/visitation_impl.hpp:173:9: instantiated from ‘typename Visitor::result_type boost::detail::variant::visitation_impl_invoke(int, Visitor&, VoidPtrCV, T*, NoBackupFlag, int) [with Visitor = boost::detail::variant::invoke_visitor<lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> > >, VoidPtrCV = void*, T = std::vector<int>, NoBackupFlag = boost::variant<boost::blank, int, std::basic_string<char>, std::vector<int> >::has_fallback_type_, typename Visitor::result_type = void]’
boost_1_46_0/boost/variant/detail/visitation_impl.hpp:260:1: instantiated from ‘typename Visitor::result_type boost::detail::variant::visitation_impl(int, int, Visitor&, VoidPtrCV, mpl_::false_, NoBackupFlag, Which*, step0*) [with Which = mpl_::int_<0>, step0 = boost::detail::variant::visitation_impl_step<boost::mpl::l_iter<boost::mpl::l_item<mpl_::long_<4l>, boost::blank, boost::mpl::l_item<mpl_::long_<3l>, int, boost::mpl::l_item<mpl_::long_<2l>, std::basic_string<char>, boost::mpl::l_item<mpl_::long_<1l>, std::vector<int>, boost::mpl::l_end> > > > >, boost::mpl::l_iter<boost::mpl::l_end> >, Visitor = boost::detail::variant::invoke_visitor<lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> > >, VoidPtrCV = void*, NoBackupFlag = boost::variant<boost::blank, int, std::basic_string<char>, std::vector<int> >::has_fallback_type_, typename Visitor::result_type = void, mpl_::false_ = mpl_::bool_<false>]’
boost_1_46_0/boost/variant/variant.hpp:1776:13: instantiated from ‘static typename Visitor::result_type boost::variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>::internal_apply_visitor_impl(int, int, Visitor&, VoidPtrCV) [with Visitor = boost::detail::variant::invoke_visitor<lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> > >, VoidPtrCV = void*, T0_ = boost::blank, T1 = int, T2 = std::basic_string<char>, T3 = std::vector<int>, T4 = boost::detail::variant::void_, T5 = boost::detail::variant::void_, T6 = boost::detail::variant::void_, T7 = boost::detail::variant::void_, T8 = boost::detail::variant::void_, T9 = boost::detail::variant::void_, T10 = boost::detail::variant::void_, T11 = boost::detail::variant::void_, T12 = boost::detail::variant::void_, T13 = boost::detail::variant::void_, T14 = boost::detail::variant::void_, T15 = boost::detail::variant::void_, T16 = boost::detail::variant::void_, T17 = boost::detail::variant::void_, T18 = boost::detail::variant::void_, T19 = boost::detail::variant::void_, typename Visitor::result_type = void]’
boost_1_46_0/boost/variant/variant.hpp:1787:13: instantiated from ‘typename Visitor::result_type boost::variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>::internal_apply_visitor(Visitor&) [with Visitor = boost::detail::variant::invoke_visitor<lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> > >, T0_ = boost::blank, T1 = int, T2 = std::basic_string<char>, T3 = std::vector<int>, T4 = boost::detail::variant::void_, T5 = boost::detail::variant::void_, T6 = boost::detail::variant::void_, T7 = boost::detail::variant::void_, T8 = boost::detail::variant::void_, T9 = boost::detail::variant::void_, T10 = boost::detail::variant::void_, T11 = boost::detail::variant::void_, T12 = boost::detail::variant::void_, T13 = boost::detail::variant::void_, T14 = boost::detail::variant::void_, T15 = boost::detail::variant::void_, T16 = boost::detail::variant::void_, T17 = boost::detail::variant::void_, T18 = boost::detail::variant::void_, T19 = boost::detail::variant::void_, typename Visitor::result_type = void]’
boost_1_46_0/boost/variant/variant.hpp:1810:52: instantiated from ‘typename Visitor::result_type boost::variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>::apply_visitor(Visitor&) [with Visitor = lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> >, T0_ = boost::blank, T1 = int, T2 = std::basic_string<char>, T3 = std::vector<int>, T4 = boost::detail::variant::void_, T5 = boost::detail::variant::void_, T6 = boost::detail::variant::void_, T7 = boost::detail::variant::void_, T8 = boost::detail::variant::void_, T9 = boost::detail::variant::void_, T10 = boost::detail::variant::void_, T11 = boost::detail::variant::void_, T12 = boost::detail::variant::void_, T13 = boost::detail::variant::void_, T14 = boost::detail::variant::void_, T15 = boost::detail::variant::void_, T16 = boost::detail::variant::void_, T17 = boost::detail::variant::void_, T18 = boost::detail::variant::void_, T19 = boost::detail::variant::void_, typename Visitor::result_type = void]’
boost_1_46_0/boost/variant/detail/apply_visitor_unary.hpp:60:43: instantiated from ‘typename Visitor::result_type boost::apply_visitor(Visitor&, Visitable&) [with Visitor = lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> >, Visitable = boost::variant<boost::blank, int, std::basic_string<char>, std::vector<int> >, typename Visitor::result_type = void]’
test2.cpp:49:40: instantiated from here
boost_1_46_0/boost/variant/variant.hpp:832:32: error: request for member ‘operator()’ is ambiguous
test2.cpp:44:54: error: candidates are: main()::<lambda(std::vector<int>&)>
test2.cpp:43:47: error: main()::<lambda(std::string&)>
test2.cpp:42:55: error: main()::<lambda(int)>
boost_1_46_0/boost/variant/variant.hpp:832:32: error: return-statement with a value, in function returning ''void''
conclusión:
Deseo agregar la versión final de esta utilidad, que incluye pruebas:
lambda_visitor.h
#include <boost/variant.hpp>
namespace Variant {
template <typename ReturnType, typename... Lambdas>
struct lambda_visitor;
template <typename ReturnType, typename Lambda1, typename... Lambdas>
struct lambda_visitor< ReturnType, Lambda1, Lambdas...>
: public lambda_visitor<ReturnType, Lambdas...>, public Lambda1 {
using Lambda1::operator();
using lambda_visitor< ReturnType, Lambdas...>::operator();
typedef ReturnType ReturnType_t;
lambda_visitor(Lambda1 l1, Lambdas... lambdas) : Lambda1(l1), lambda_visitor< ReturnType, Lambdas...> (lambdas...) {
}
lambda_visitor(Lambda1 && l1, Lambdas && ... lambdas) : Lambda1(l1), lambda_visitor< ReturnType, Lambdas...> (lambdas...) {
}
};
template <typename ReturnType, typename Lambda1>
struct lambda_visitor<ReturnType, Lambda1>
: public boost::static_visitor<ReturnType>, public Lambda1 {
using Lambda1::operator();
typedef ReturnType ReturnType_t;
lambda_visitor(Lambda1 l1) : boost::static_visitor<ReturnType > (), Lambda1(l1) {
}
lambda_visitor(Lambda1 && l1) : boost::static_visitor<ReturnType > (), Lambda1(l1) {
}
};
template <typename ReturnType>
struct lambda_visitor<ReturnType> : public boost::static_visitor<ReturnType> {
typedef ReturnType ReturnType_t;
lambda_visitor() : boost::static_visitor<ReturnType > () {
}
};
template <typename ReturnType>
struct default_blank_visitor {
typedef ReturnType ReturnType_t;
inline ReturnType operator() (const boost::blank&) const {
return (ReturnType) 0;
};
};
template<>
struct default_blank_visitor<void> {
typedef void ReturnType_t;
inline void operator() (const boost::blank&) const {};
};
template <typename ReturnType, typename... Lambdas>
lambda_visitor<ReturnType, default_blank_visitor< ReturnType >, Lambdas...> make_lambda_visitor(Lambdas... lambdas) {
return
{
default_blank_visitor<ReturnType > (), lambdas...
};
// you can use the following instead if your compiler doesn''t
// support list-initialization yet
//return lambda_visitor<ReturnType, default_blank_visitor<ReturnType> , Lambdas...>( default_blank_visitor<ReturnType>(), lambdas...);
};
/*
template <typename ReturnType, typename... Lambdas>
lambda_visitor<ReturnType, default_blank_visitor< ReturnType >, Lambdas...> make_lambda_visitor(Lambdas && ... lambdas) {
return
{
default_blank_visitor<ReturnType > (), lambdas...
};
// you can use the following instead if your compiler doesn''t
// support list-initialization yet
//return lambda_visitor<ReturnType, default_blank_visitor<ReturnType> , Lambdas...>( default_blank_visitor<ReturnType>(), lambdas...);
};*/
template <typename ReturnType, typename... Lambdas>
lambda_visitor<ReturnType, Lambdas...> make_lambda_visitor_override_blank(Lambdas... lambdas) {
return
{
lambdas...
};
// you can use the following instead if your compiler doesn''t
// support list-initialization yet
//return lambda_visitor<ReturnType, Lambdas...>(lambdas...);
}
namespace basic_usage
{
struct Test
{
typedef boost::variant< boost::blank , int , double > variant_t;
void run()
{
variant_t a, b, c;
a = 42;
b = 3.14159265;
auto visitor = Variant::make_lambda_visitor<int>( [](int v) -> int { return v+1; } , [](double v) -> int { return (int)v*2; } );
int result = boost::apply_visitor(visitor, a);
HAssertMsg( result == (42 + 1) , "unexpected");
result = boost::apply_visitor( visitor , b);
HAssertMsg( result == 6 , "unexpected");
auto blankVisitor = Variant::make_lambda_visitor_override_blank<int>(
[](int v) -> int { return -1; }
, [](double v) -> int { return -1; }
, [](boost::blank ) -> int { return 0;} );
result = boost::apply_visitor( blankVisitor , c);
HAssertMsg( result == 0 , "unexpected");
//same as previous case, but using lambda coalescing :-)
auto blankVisitor2 = Variant::make_lambda_visitor_override_blank<int>(
[](boost::variant< int , double >& v) -> int { return -1; }
, [](boost::blank ) -> int { return 0;} );
result = boost::apply_visitor( blankVisitor2 , c);
HAssertMsg( result == 0 , "unexpected");
result = boost::apply_visitor( blankVisitor2 , a);
HAssertMsg( result == -1 , "unexpected");
result = boost::apply_visitor( blankVisitor2 , b);
HAssertMsg( result == -1 , "unexpected");
}
};
}
};