example - templates en c++
usando declaraciĆ³n en plantilla variadica (2)
Esta pregunta está inspirada en la siguiente solución para la pseudoambigüedad de sobrecarga de herencia, que es una buena manera de implementar visitantes lambda para boost :: variant como se propone en esta respuesta :
Quiero hacer algo como lo siguiente:
template <typename ReturnType, typename... Lambdas>
struct lambda_visitor : public boost::static_visitor<ReturnType>, public Lambdas... {
using Lambdas...::operator(); //<--- doesn''t seem to work
lambda_visitor(Lambdas... lambdas) : boost::static_visitor<ReturnType>() , Lambdas(lambdas)... { }
};
No estoy seguro de cuál sería la sintaxis correcta de agregar cláusulas para listas de tipos empaquetadas. La cláusula de using
es crucial para evitar que el compilador se queje de que el operator()
es ambiguo, lo que no lo es, porque tienen todas las firmas diferentes.
Esta es una vieja pregunta y una gran respuesta. Hay una cosa más que podemos hacer para mejorar IMHO.
En c ++ 14 y mejor, no necesitamos especificar el tipo de retorno, se puede deducir.
#include <boost/variant.hpp>
#include <type_traits>
namespace detail {
template<typename... Lambdas>
struct lambda_visitor;
template<typename Lambda1, typename... Lambdas>
struct lambda_visitor<Lambda1, Lambdas...>
: public lambda_visitor<Lambdas...>,
public Lambda1
{
using Lambda1::operator ();
using lambda_visitor<Lambdas...>::operator ();
lambda_visitor(Lambda1 l1, Lambdas... lambdas)
: Lambda1(l1)
, lambda_visitor<Lambdas...>(lambdas...) {}
};
template<typename Lambda1>
struct lambda_visitor<Lambda1>
:
public Lambda1
{
using Lambda1::operator ();
lambda_visitor(Lambda1 l1)
: Lambda1(l1) {}
};
}
template<class...Fs>
auto compose(Fs&& ...fs)
{
using visitor_type = detail::lambda_visitor<std::decay_t<Fs>...>;
return visitor_type(std::forward<Fs>(fs)...);
};
caso de uso:
boost::variant<int, std::string> x = "foo", y = 4;
auto visitor = compose([](const int& i)
{
std::cout << i << std::endl;
},
[](const std::string& s)
{
std::cout << s << std::endl;
});
boost::apply_visitor(visitor, x);
boost::apply_visitor(visitor, y);
Ok, encontré una solución bastante decente:
básicamente necesito desempaquetar un caso lambda extra y aplicar la cláusula de using
al lambda desempaquetado y al resto, pero en este caso, ya que aparentemente no puedo hacer una lista variada de declaraciones de uso (al menos no conozco la sintaxis, si es posible), el resto se envuelve heredando del caso ''resto'', como este:
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();
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();
lambda_visitor(Lambda1 l1)
: boost::static_visitor<ReturnType>(), Lambda1(l1)
{}
};
template <typename ReturnType>
struct lambda_visitor<ReturnType>
: public boost::static_visitor<ReturnType> {
lambda_visitor() : boost::static_visitor<ReturnType>() {}
};
Así que puedo hacer esto de forma inductiva colocando dos declaraciones de uso, una del tipo lambda desempaquetado y otra de la clase principal, que en realidad es la misma clase con una lambda menos.