Pregunta

Tengo una clase que se puede decorar con un conjunto de plantillas complementarias para proporcionar funcionalidad adicional.Cada complemento tiene un addon_value identificador que la clase base necesita conocer.

El siguiente código es un ejemplo de lo que me gustaría hacer.Obviamente, la función main() no se puede compilar.El objetivo es que CBase::GetValueOfAddOns() conozca el valor de OR-ing del addon_value para cada complemento.En realidad, no es necesario realizar el cálculo en GetValueOfAddOns(), solo debe poder obtener el resultado.

template< class T >
class AddOn_A : public T
{
public: 
    AddOn_A( int x ) : T( x ) 
    {};

    enum { addon_value = 0x00000001 };
};

template< class T >
class AddOn_B : public T
{
public: 
    AddOn_B( int x ) : T( x ) 
    {};

    enum { addon_value = 0x00000010 };
};

class CBase
{
public:
    explicit CBase( int x ) : x_( x ) 
    {
        // error LNK2001: unresolved external symbol "public: virtual int __thiscall CBase::GetValueOfAddOns(void)const " (?GetValueOfAddOns@CBase@@UBEHXZ)
        int z = GetValueOfAddOns();
    };

    virtual int GetValueOfAddOns() const = 0;

private:
    int x_;
};

// define an empty AddOn
template< class > class empty 
{
public:
    enum { addon_value = 0x00000000 };
};

// forward declaration and Add-On defaults
template< template< class > class AddOn1 = empty,
          template< class > class AddOn2 = empty,
          template< class > class AddOn3 = empty >
class CMyClass;

// specialized template for the default case
template<> class CMyClass< empty, empty, empty > : public CBase
{
public:
    CMyClass( int x ) : CBase( x ) 
    {};

    enum { addon_value = 0x00000000 };
};

// actual definition
template< template< class > class AddOn1,
          template< class > class AddOn2,
          template< class > class AddOn3 >
class CMyClass : public AddOn1< CBase >,
                 public CMyClass< AddOn2, AddOn3 >
{
public:
    CMyClass( int x ) : AddOn1< CBase >( x ),
                        CMyClass< AddOn2, AddOn3 >( x )
    {};

    enum { addon_value = AddOn1< CBase >::addon_value | CMyClass< AddOn2, AddOn3 >::addon_value };

    int GetValueOfAddOns() const
    {
        return addon_value;
    }
};

int _tmain( int argc, _TCHAR* argv[] )
{
    CMyClass< AddOn_A > A( 0 );
    _ASSERT( A.GetValueOfAddOns() == AddOn_A< CBase >::addon_value );

    CMyClass< AddOn_A, AddOn_B > AB( 0 );
    _ASSERT( AB.GetValueOfAddOns() == ( AddOn_A< CBase >::addon_value | AddOn_B< CBase >::addon_value ) );

    return 0;
}

Gracias por cualquier ayuda, PaulH

¿Fue útil?

Solución

No estoy seguro de que esta sea la forma más elegante, pero lo siguiente es bastante sencillo:

Agregue esto a CMyClass:

enum {AddonsValues = AddOn1<CBase>::addon_value | CMyClass<AddOn2, AddOn3>::AddonsValues};

int GetValueOfAddOns()
{
    // return the result of OR-ing the addon_value of each add-on.
    return AddonsValues;
};

y esto al CMyClass<empty, empty, empty> especializado:

enum {AddonsValues = 0};

Otros consejos

Si hace que esa función sea pura virtual, puede implementarla en CMyClass, donde tiene toda la información disponible. Simplemente cambie su clase empty para definir enum { addon_value = 0x00000000 };, lo que facilitará esto.

Hagámoslo mejor:

Vea su nueva pregunta sobre El problema con la jerarquía aquí. También resolví el problema de AddOnValues ​​allí y, en realidad, no hay necesidad de metaprogramación de plantillas.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top