75 votes

Polymorphisme statique C ++ (CRTP) et utilisation de typedefs à partir de classes dérivées

J'ai lu l' article de Wikipedia sur l'curieusement récurrents pattern template en C++ pour faire du statique (lire: au moment de la compilation) le polymorphisme. Je voulais généraliser afin que je puisse changer le type de retour des fonctions basées sur le type dérivé. (Il semble que ce devrait être possible, étant donné le type de base connaît le type dérivé à partir du paramètre de modèle). Malheureusement, le code suivant ne compile pas à l'aide de MSVC 2010 (je n'ai pas un accès facile à la gcc en ce moment donc je n'ai pas encore essayé). Quelqu'un sait pourquoi?

template <typename derived_t>
class base {
public:
    typedef typename derived_t::value_type value_type;
    value_type foo() {
        return static_cast<derived_t*>(this)->foo();
    }
};

template <typename T>
class derived : public base<derived<T> > {
public:
    typedef T value_type;
    value_type foo() {
        return T(); //return some T object (assumes T is default constructable)
    }
};

int main() {
    derived<int> a;
}

BTW, j'ai un travail autour de l'aide supplémentaire des paramètres de modèle, mais je ne l'aime pas---il sera très bavard lors du passage de nombreux types de la chaîne d'héritage.

template <typename derived_t, typename value_type>
class base { ... };

template <typename T>
class derived : public base<derived<T>,T> { ... };

EDIT:

Le message d'erreur que MSVC 2010 donne dans cette situation est - error C2039: 'value_type' : is not a member of 'derived<T>'

g++ 4.1.2 (via codepad.org), dit - error: no type named 'value_type' in 'class derived<int>'

76voto

James McNellis Points 193607

derived est incomplète lorsque vous l'utiliser comme un argument de modèle à l' base de ses classes de base de la liste.

Une commune de la solution de contournement est d'utiliser une classe de traits du modèle. Voici votre exemple, traitsified. Cela montre comment vous pouvez utiliser les deux types et des fonctions de la classe dérivée à travers les traits.

// Declare a base_traits traits class template:
template <typename derived_t> 
struct base_traits;

// Define the base class that uses the traits:
template <typename derived_t> 
struct base { 
    typedef typename base_traits<derived_t>::value_type value_type;
    value_type base_foo() {
        return base_traits<derived_t>::call_foo(static_cast<derived_t*>(this));
    }
};

// Define the derived class; it can use the traits too:
template <typename T>
struct derived : base<derived<T> > { 
    typedef typename base_traits<derived>::value_type value_type;

    value_type derived_foo() { 
        return value_type(); 
    }
};

// Declare and define a base_traits specialization for derived:
template <typename T> 
struct base_traits<derived<T> > {
    typedef T value_type;

    static value_type call_foo(derived<T>* x) { 
        return x->derived_foo(); 
    }
};

Vous avez juste besoin de se spécialiser base_traits pour tous les types que vous utilisez pour l'argument de modèle derived_t de base et assurez-vous que chaque spécialisation offre à tous les membres qu' base nécessite.

-4voto

iammilind Points 29275

Vous pouvez éviter de passer 2 arguments dans template . Dans CRTP, si vous avez l'assurance que class base sera jumelé avec class derived (et non avec class derived_2 ), utilisez la technique ci-dessous:

 template <typename T> class derived;  // forward declare

template <typename value_type, typename derived_t = derived<value_type> >
class base {  // make class derived as default argument
    value_type foo();
};
 

Usage:

 template <typename T>
class derived : public base<T> // directly use <T> for base
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X