ver una tiene siguientes reutilización redefinición qué que poo miembros metodos los hijo herencia heredados hereda destructores derivadas derivada código constructores con clases clase caracteristicas c++

c++ - una - redefinición de metodos en clases derivadas



¿Qué significa heredar de lambda? (3)

Bueno, ese código se compilará, pero el problema es que no podrá construir por defecto ningún objeto de esa clase 1 , porque el constructor de lambda no es accesible (aparte de los constructores copiar / mover) . Los únicos constructores garantizados por un tipo lambda son los constructores de copia / movimiento predeterminados . Y no hay constructor predeterminado

[expr.prim.lambda/21]

El tipo de cierre asociado con una expresión lambda no tiene un constructor predeterminado y un operador de asignación de copia eliminado. Tiene un constructor de copia predeterminado y un constructor de movimiento predeterminado ([class.copy]). [Nota: Estas funciones miembro especiales se definen implícitamente como de costumbre y, por lo tanto, pueden definirse como eliminadas. - nota final]

o de cppreference :

//ClosureType() = delete; //(until C++14) ClosureType(const ClosureType& ) = default; //(since C++14) ClosureType(ClosureType&& ) = default; //(since C++14)

La historia de que el constructor lambda es inaccesible se remonta a su propuesta inicial, que se encuentra here

En la sección 3, segundo párrafo, y cito:

En esta traducción, __some_unique_name es un nombre nuevo, que no se usa en ninguna otra parte del programa de manera que pueda causar conflictos con su uso como tipo de cierre. Este nombre y el constructor de la clase no necesitan exponerse al usuario: las únicas características en las que el usuario puede confiar en el tipo de cierre son un constructor de copia (y un constructor de movimiento si se aprueba esa propuesta) y operador de llamada de función. Los tipos de cierre no necesitan constructores predeterminados, operadores de asignación o cualquier otro medio de acceso más allá de las llamadas a funciones. Puede valer la pena que la implementabilidad prohíba la creación de clases derivadas de tipos de cierre. ...

Como puede ver, la propuesta incluso sugirió que la creación de clases derivadas de tipos de cierre debería estar prohibida.

1, por supuesto, puede copiar-inicializar la clase base con a para inicializar un objeto de tipo B Ver this

Ahora, a tu pregunta:

¿Puede esto ser útil de alguna manera?

No en tu forma exacta. La suya solo será instanciable con la instancia a . Sin embargo, si hereda de una Clase invocable genérica, como un tipo lambda, hay dos casos en los que puedo pensar.

  1. Cree un Functor que llame a un grupo de functores en una secuencia de herencia dada:

    Un ejemplo simplificado:

    template<typename TFirst, typename... TRemaining> class FunctionSequence : public TFirst, FunctionSequence<TRemaining...> { public: FunctionSequence(TFirst first, TRemaining... remaining) : TFirst(first), FunctionSequence<TRemaining...>(remaining...) {} template<typename... Args> decltype(auto) operator () (Args&&... args){ return FunctionSequence<TRemaining...>::operator() ( TFirst::operator()(std::forward<Arg>(args)...) ); } }; template<typename T> class FunctionSequence<T> : public T { public: FunctionSequence(T t) : T(t) {} using T::operator(); }; template<typename... T> auto make_functionSequence(T... t){ return FunctionSequence<T...>(t...); }

    ejemplo de uso:

    int main(){ //note: these lambda functions are bug ridden. Its just for simplicity here. //For correct version, see the one on coliru, read on. auto trimLeft = [](std::string& str) -> std::string& { str.erase(0, str.find_first_not_of('' '')); return str; }; auto trimRight = [](std::string& str) -> std::string& { str.erase(str.find_last_not_of('' '')+1); return str; }; auto capitalize = [](std::string& str) -> std::string& { for(auto& x : str) x = std::toupper(x); return str; }; auto trimAndCapitalize = make_functionSequence(trimLeft, trimRight, capitalize); std::string str = " what a Hullabaloo "; std::cout << "Before TrimAndCapitalize: str = /"" << str << "/"/n"; trimAndCapitalize(str); std::cout << "After TrimAndCapitalize: str = /"" << str << "/"/n"; return 0; }

    salida

    Before TrimAndCapitalize: str = " what a Hullabaloo " After TrimAndCapitalize: str = "WHAT A HULLABALOO"

    Véalo en vivo en Coliru

  2. Cree un Functor con un operator()(...) sobrecargado operator()(...) , sobrecargado con el operator()(...) todas las clases base operator()(...) :

Otro truco genial: dado que el tipo resultante de make_functionSequence(...) es una clase invocable. Puede agregar más lambda o invocable en otro momento.

//.... As previously seen auto trimAndCapitalize = make_functionSequence(trimLeft, trimRight, capitalize); auto replace = [](std::string& str) -> std::string& { str.replace(0, 4, "Whaaaaat"); return str; }; //Add more Functors/lambdas to the original trimAndCapitalize auto replaced = make_functionSequence(trimAndCapitalize, replace /*, ... */); replaced(str2);

Encontré este código que parece ser interesante:

auto a = [](){}; class B : decltype(a) { };

Quiero saber que hace. ¿Puede esto ser útil de alguna manera?


En realidad, esto puede ser bastante útil, pero depende de cuán directo desee ser sobre todo el asunto. Considere el siguiente código:

#include <boost/variant.hpp> #include <iostream> #include <string> #include <unordered_map> template <class R, class T, class ... Ts> struct Inheritor : public T, Inheritor<R, Ts...> { using T::operator(); using Inheritor<R, Ts...>::operator(); Inheritor(T t, Ts ... ts) : T(t), Inheritor<R, Ts...>(ts...) {} }; template <class R, class T> struct Inheritor<R, T> : public boost::static_visitor<R>, T { using T::operator(); Inheritor(T t) : T(t) {} }; template <class R, class V, class ... T> auto apply_visitor_inline(V& v, T ... t) { Inheritor<R, T...> i(t...); return boost::apply_visitor(i, v); } int main() { boost::variant< int, std::string > u("hello world"); boost::variant< int, std::string > u2(5); auto result = apply_visitor_inline<int64_t>(u, [] (int i) { return i;}, [] (const std::string& s) { return s.size();}); auto result2 = apply_visitor_inline<int64_t>(u2, [] (int i) { return i;}, [] (const std::string& s) { return s.size();}); std::cout << result; std::cout << result2; }

El fragmento de su pregunta no aparece en forma exacta en ningún lado. Pero puede ver que los tipos de lambdas se infieren en apply_visitor_inline . Luego se instancia una clase que hereda de todas estas lambdas. ¿El propósito? Podemos combinar múltiples lambdas en una sola, con el propósito de cosas como apply_visitor . Esta función espera recibir un único objeto de función que defina múltiples operator() y distinga entre ellos en función de la sobrecarga. Pero a veces es más conveniente definir una lambda que opera en cada uno de los tipos que tenemos que cubrir. En ese caso, la herencia de lambdas proporciona un mecanismo para combinar.

Obtuve la idea del visitante en línea desde aquí: https://github.com/exclipy/inline_variant_visitor , aunque no miré la implementación allí, por lo que esta implementación es mía (pero supongo que es muy similar).

Editar: el código publicado originalmente solo funcionó debido a un error en el sonido metálico. De acuerdo con esta pregunta ( Lambdas sobrecargadas en C ++ y diferencias entre clang y gcc ), la búsqueda de operator() múltiples operator() en clases base es ambigua, y de hecho el código que publiqué originalmente no se compiló en gcc. El nuevo código se compila en ambos y debe ser compatible. Lamentablemente, aparentemente no hay forma de hacer una declaración de uso variable, por lo que debe usarse la recursividad.


Lambdas son objetos de función debajo, con azúcar sintética adicional. a evalúa algo así (con el nombre MyLambda como un nombre aleatorio, al igual que cuando MyLambda un namespace {} - el nombre del espacio de nombres será aleatorio):

class MyLambda { public: void operator()() { } }

Entonces, cuando hereda de una lambda, lo que está haciendo es heredar de una clase / estructura anónima.

En cuanto a la utilidad, bueno, es tan útil como cualquier otra herencia. Puede tener la funcionalidad de múltiples lambdas con herencia múltiple en un objeto, puede agregarle nuevos métodos para extenderlo. No puedo pensar en ninguna aplicación real en este momento, pero estoy seguro de que hay muchas.

Consulte esta pregunta para más información.