2011-12-20 15 views
5

Para ser honesto, no se sabe muy bien, cómo hacer esta pregunta, así que por favor no seas loco :)C++ en función de los padres del niño de retorno

De todos modos, quiero tener las mutators (set) en mi clase para devolver this para permitir jQuery-like a.name("something").address("somethingelse"); Tengo una clase para padres (Entity) y varios childclasses (Client, Agent etc.). Los mutadores de la mayoría de las cosas se heredan de la clase Entity (como el nombre o la dirección), pero devuelven un objeto Entity, por lo que no puedo llamar mutantes Client en ellos.

En otras palabras:

// name mutator 
Entity& Entity::name(const string& name) { 
    // [...] checks 
    _name = name; 
    return *this; 
} 

// budgetRange mutator 
Client& Client::budgetRange(const long int& range) { 
    // [...] checks 
    _budgetRange = range; 
    return *this; 
} 

luego, cuando lo llamo:

Client a; a.name("Dorota Adamczyk").budgetRange(50); 

El compilador (lógicamente) dice que el objeto Entidad tiene ningún miembro budgetRange (porque el nombre devuelve una entidad, no es un cliente).

Mi pregunta ahora es: ¿cómo podría implementar algo como esto? Pensé en la sobrecarga de todas las funciones de entidad en el childclasses pero eso no estaría bien y estaría en contra de la idea de la herencia :)

Gracias de antemano por sus ideas: D

+0

google también para 'method chaining' y' named parameter idiom', que es lo que estás haciendo.Tenga en cuenta que, al heredar de 'Cliente' su código se romperá de nuevo utilizando la solución CRTP a continuación. – cheind

Respuesta

7

Debe usar el CRTP.

template<class Derived> 
class Entity 
{ 
    Derived* This() { return static_cast<Derived*>(this); } 

public: 
    Derived& name(const string& name) 
    { 
     ... 
     return *This(); 
    } 
}; 

class Client : public Entity<Client> 
{ 
public: 
    Client& budgetRange(const long& range) 
    { 
     ...  
     return *this; 
    } 
}; 

Si desea utilizar las funciones virtuales, también se puede agregar la clase base abstracta, como esto:

class AbstractEntity 
{ 
public: 
    virtual void foo() = 0; 

    virtual ~AbstractEntity(); 
}; 

template<class Derived> 
class Entity : AbstractEntity 
{...}; 
+0

Me gusta mucho esta idea (especialmente la función This() ^^). Muchas gracias, eso es lo que estaba buscando :) – Asmodiel

+0

Excelentes cosas. He estado buscando un buen ejemplo de CRTP. Gracias. – sje397

+0

@ sje397 Otro buen ejemplo de CRTP es una clase de árbol genérico, que proporciona implementaciones concretas de nodos a través de 'template class node'. Por ejemplo: 'D & node :: left()' puede proporcionar el nodo concreto impl. De esa forma puede rellenar toda la lógica de árbol en la clase de nodo genérica. – cheind

3

La "plantilla curiosamente recursiva "el patrón podría ayudar aquí; hacer que la clase base de una plantilla, parametrizado por la clase derivada, a lo largo de las líneas de:

template <typename Derived> 
struct Entity { 
    Derived & name(std::string const & name) { 
     // stuff 
     return static_cast<Derived&>(*this); 
    } 
}; 

struct Client : Entity<Client> { 
    Client & budget(long range) { 
     // stuff 
     return *this; 
    } 
}; 

Client().name("Mike").budget(50); // should compile 

Esto sólo funcionará si todos sus tipos heredan directamente de Entity. Si necesita que los tipos sean polimórficos (es decir, que todos compartan una clase base común), tendrá que agregar otra clase base que no sea de plantilla y heredar de ella Entity.

+0

Muchas gracias por su respuesta, pero elegiré Abyx 'uno ya que incluyó la idea this() :) Upvoted though. – Asmodiel

2

Ahora que casi todo lo que ya se ha dicho, quiero añadir un pedazo de respuesta que permite usar el CRTP a través de múltiples niveles de herencia:

las implementaciones anteriores CRTP se rompen cuando uno quiere heredar de Client, ya Derived se referirá a Client. En caso de que quiera ser capaz de llevar el lenguaje parámetro llamado a través de múltiples niveles de herencia con el patrón CRTP, es necesario codificar sus clases al igual que

template<class Derived> 
class Entity_T 
{ 
protected: 
    Derived* This() { return static_cast<Derived*>(this); } 
public: 
    Derived& name(const string& name) 
    { 
     ... 
     return *This(); 
    } 
}; 

template<class Derived> 
class Client_T : public Entity_T<Derived> 
{ 
    Derived& budgetRange(const long& range) 
    { 
     ...  
     return *This(); 
    } 
}; 

Para proporcionar al usuario una versión libre de la plantilla de Client_T añadir

class Client : public Client_T<Client> {}; 

Tanto si vale la pena la base de código ampliada es totalmente suya. Tenga en cuenta que no he compilado el código anterior.

Cuestiones relacionadas