26

Esta pregunta se inspira en el following solution a la herencia múltiple sobrecarga pseudo-ambigüedad, que es una buena manera de poner en práctica los visitantes lambda de impulso :: variante como se propone en this answer:mediante declaración en la plantilla variadic

que quiero hacer algo así 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 lo que sería la sintaxis correcta de añadir cláusulas que utilizan para las listas de tipo empaquetados. La cláusula using es crucial para evitar que el compilador se queje de que el operator() es ambiguo, lo que no es cierto, porque tienen todas las firmas diferentes.

+3

hay un "paquete de expansión en el uso de declaraciones-" [P0195R2] (http://wg21.link/p0195r2) propuesta. – Orient

+1

La expansión del paquete de parámetros en el contexto de una declaración 'using' está en el estándar C++ 17. – ThomasMcLeod

Respuesta

31

Ok He encontrado una solución bastante decente:

Necesito básicamente para desempaquetar un caso lambda extra y aplicar la cláusula using a la lambda desempaquetado y el resto, pero en este caso, ya que al parecer no puedo hacer una lista variadic de utilizar declaraciones (al menos no sé la sintaxis, si su posible), el resto se envuelve heredando de la caja 'resto', así:

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>() {} 
}; 

por lo que puedo hacer esto de forma inductiva colocando dos declaraciones de uso, una del tipo lambda sin empaquetar y otra de la clase principal, que en realidad es la misma clase con una lambda menos.

+0

Ah, vi tus comentarios en mi respuesta y estaba a punto de publicar esta solución en este momento :) Lástima que la solución elegante se pone un poco peluda con esto :( –

+0

@RMartinho, no realmente, todavía es una solución bastante agradable, gracias – lurscher

+0

Gracias a ¡usted también! ¡Aprendí otro caso raro de la esquina de C++! :) –

5

Esta es una vieja pregunta y una gran respuesta. Hay una cosa más que podemos hacer para mejorarlo en mi humilde opinión.

En C++ 14 y mejor, no es necesario especificar el tipo de devolución, 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); 
Cuestiones relacionadas