2010-04-13 9 views
14

En un operador de comparación:C++: Cómo requiere que un tipo de plantilla se deriva de la otra

template<class R1, class R2> 
bool operator==(Manager<R1> m1, Manager<R2> m2) { 
    return m1.internal_field == m2.internal_field; 
} 

¿Hay alguna manera de que pudiera cumplir esa R1 y R2 deben tener una relación supertipo o subtipo? Es decir, me gustaría permitir que R1 se derive de R2 o que R2 se derive de R1, pero no permite la comparación si R1 y R2 son tipos no relacionados.

+0

yo sepa C++ no admite en tiempo de ejecución limitaciones de los parámetros de plantilla como aquellos que estás pidiendo ¿Quizás RTTI ('typeid') o' dynamic_cast' podría usarse para ese fin en tiempo de ejecución? – stakx

+2

@stakx: ¿Qué tiene esto que ver con el tiempo de ejecución? R1 y R2 son conocidos en tiempo de compilación. – GManNickG

+0

Puede usar información en tiempo de compilación para verificar que, consulte la biblioteca de verificación del concepto de impulso (enlace en mi respuesta). No hay necesidad de RTTI cuando se trata de tipos utilizados en la compilación como en este ejemplo. – Klaim

Respuesta

24

un rasgo que desea puede tener este aspecto:

template <typename B, typename D> 
struct is_base_of // check if B is a base of D 
{ 
    typedef char yes[1]; 
    typedef char no[2]; 

    static yes& test(B*); 
    static no& test(...); 

    static D* get(void); 

    static const bool value = sizeof(test(get()) == sizeof(yes); 
}; 

continuación, sólo tiene una aserción estática de algún tipo:

// really basic 
template <bool> 
struct static_assert; 

template <> 
struct static_assert<true> {}; // only true is defined 

#define STATIC_ASSERT(x) static_assert<(x)>() 

a continuación, poner los dos juntos:

template<class R1, class R2> 
bool operator==(Manager<R1> m1, Manager<R2> m2) 
{ 
    STATIC_ASSERT(is_base_of<R1, R2>::value || is_base_of<R2, R1>::value); 

    return p1.internal_field == p2.internal_field; 
} 

Si uno no deriva del otro, la función no se compilará. (Su error será similar a "static_assert<false> no definido", y apuntará a esa línea.)

+2

Astucia. (La última línea debe ser 'static_assert ' no 'static_cast , ¿no?) –

+0

@dash: Vaya, sí. @gf: Gracias. :) – GManNickG

+0

Tenga cuidado con el uso de static_assert como nombre para cualquier cosa. Va a ser una palabra clave en C++ 0x. –

1

Si concepts se hubiera incluido en C++ 0x, es posible que haya podido utilizarlos con un compilador que los implemente (como gcc).

Como no es el caso, la única alternativa actualmente disponible para hacer lo que desea parece ser la biblioteca Boost Concept Check.

21

Puede usar los tipos de impulso (is_base_of) y aumentar enable_if.

#include <boost/type_traits.hpp> 
#include <boost/utility/enable_if.hpp> 

template <class R1, class R2> 
struct has_derived_base_relationship : 
    boost::integral_constant< 
     bool, boost::is_base_of<R1, R2>::value || boost::is_base_of<R2, R1>::value 
    > 
{}; 

template<class R1, class R2> 
typename boost::enable_if<has_derived_base_relationship<R1, R2>, bool>::type 
operator==(Manager<R1> m1, Manager<R2> m2) { 
    return p1.internal_field == p2.internal_field; 
} 

Por otro lado, ¿por qué el operador == uso tiene más valor con los tipos del mismo árbol de herencia? ¿No tendría que usar doble despacho para lograr resultados significativos?

+7

+1 En caso de duda, ver boost. –

1
template<class T, class B> struct Derived_from { 
static void constraints(T* p) { B* pb = p; } 
Derived_from() { void(*p)(T*) = constraints; } 
}; 

template<class R2, class R1> 
bool test(R1& r1) { 
Derived_from<R1,R2>(); // accept if R1 is derived from R2 
return false; 
} 

class Base { 
public: 
virtual ~Base() { } 
}; 

class Derived : public Base { 

}; 

class Other { 

}; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
Derived d; 
Other o; 

test<Base>(d); // OK 
test<Base>(o); // Fails in VC++ 2005 

return 0; 
} 

Los créditos van a http://www2.research.att.com/~bs/bs_faq2.html#constraints

1

debo admitir, no veo la motivación detrás de esto, sobre todo si se requiere escribir shedloads de apoyo código. Por su operador:

template<class R1, class R2> 
bool operator==(Manager<R1> m1, Manager<R2> m2) { 
    return p1.internal_field == p2.internal_field; 
} 

para compilar sin una advertencia, ambos tipos de parámetros de plantilla deben ser capaces de ser los parámetros de la plantilla Manager y esos tipos deben tener los miembros privados (supongo p1 & p2 deben m1 & m2) llamado internal_field. Dadas esas limitaciones, ¿cuál es la posibilidad de que esta función de plantilla pueda ser llamada por accidente en el (los) tipo (s) incorrecto (s)?

+0

Intencionalmente oscurecí mis intenciones en la pregunta original, en para evitar el seguimiento lateral de la discusión :) 'Manager ' era un nombre de clase ficticio. La aplicación real estaba haciendo un objeto similar a un puntero 'index_ptr ' donde la representación interna era un índice 'size_t' en lugar de una dirección de memoria. Comparar dos punteros de tipo no relacionado no es muy significativo, por eso es por lo que quería prohibirlo, pero nada sobre 'size_t' lo evitaría. – Will

+0

@Will: Consideré una situación similar hace un tiempo. La parte difícil es cuando tienes herencia múltiple 'Derived: Base1, Base2'.Un 'Base1 *' y un 'Base2 *' no están obviamente relacionados. – MSalters

3

En este post considero el problema de comprobar si un tipo coincide exactamente con otro, no es exactamente lo que se solicita, pero es más simple y espero que pueda ayudar a comprender los trucos de plantilla aplicados.

Como se hizo en boost, se pueden adoptar especializaciones de plantilla para esa tarea, de hecho, puede definir una estructura de plantilla que contenga operaciones en un tipo dado, y utilizar estructuras de plantilla anidadas para esas operaciones.En nuestro caso:

// Working on a specific type: 
template <typename T1> 
struct is_type { 
    // For all types T2!=T1 produce false: 
    template <typename T2> 
    struct same_of { static const bool value = false; }; 
    // Specialization for type T2==T1 producing true: 
    template <> 
    struct same_of<T1> { static const bool value = true; }; 
}; 

Definición de una macro permite utilizarlo fácilmente:

#define is_type_same(T1,T2) (is_type<T1>::same_of<T2>::value) 

de la siguiente manera:

template<class R1, class R2> 
bool operator==(Manager<R1> m1, Manager<R2> m2) { 
    return is_type_same(R1,R2) && m1.internal_field == m2.internal_field; 
} 
Cuestiones relacionadas