2012-03-10 9 views
6

En mi proyecto, quiero implementar una clase de proxy de plantilla de algunas clases más grandes existentes. Las clases existentes son clases de biblioteca, por lo que no se pueden modificar. En la mayoría de los casos, los clientes no saben que los objetos son instancias de clase proxy o clase más grande. En algunos casos, sin embargo, los clientes DEBEN conocer la información detallada de la clase. Dado que la clase proxy es en sí misma una clase de plantilla, no creo que la simple sobrecarga de funciones por nombre de clase pueda resolver este problema. La posible solución que pensé es agregar una clase interna anidada o typedef dentro de la clase proxy, y el cliente verifica si esta clase/typedef existe para obtener la información de la clase. Mi pregunta es: ¿cómo comprobar si una clase ha especificado una definición de clase anidada o typedef en C++ 11?¿Cómo comprobar si una clase ha especificado una definición de clase anidada o typedef en C++ 11?

Los siguientes códigos muestran un ejemplo:

#include <iostream> 
#include <functional> 
#include <string> 
#include <vector> 
#include <type_traits> 

typedef std::string CBig1; // use string for demonstration 
typedef std::string CBig2; // use string for demonstration 

//class CBig1; // the bigger class 1, codes of which can not be changed 
//class CBig2; // the bigger class 2, codes of which can not be changed 

template <typename _Big, typename _Other> 
class CProxy 
{ 
public: 
    struct proxy_tag { }; 
}; 

// how to implement this ? 
// the proxy traits class, if defined _T::proxy_tag, the ``type'' will be std::true_type, otherwise the ``type'' will be std::false_type 
template <typename _T> 
struct is_proxy 
{ 
    //typedef std::true_type type; 
    //typedef std::false_type type; 
}; 

template <typename _T> 
void ClientHelp(const _T& t, std::false_type) 
{ 
    // process real class 
    std::cerr << "real class" << std::endl; 
} 

template <typename _T> 
void ClientHelp(const _T& t, std::true_type) 
{ 
    // process proxy class 
    std::cerr << "proxy class" << std::endl; 
} 

template <typename _T> 
void Client(const _T& t) 
{ 
    ClientHelp(t, typename is_proxy<_T>::type()); 
} 

int main(int argc, char* argv[]) 
{ 
    CBig1 b; 
    CProxy<CBig1, int> p; 
    Client(b); 
    Client(p); 
    return 0; 
} 

cómo implementar la clase rasgos is_proxy?

+0

Utilice [SFINAE] (http://en.wikipedia.org/wiki/SFINAE). – iammilind

+3

No use nombres como '_This', están reservados para la implementación. – Xeo

Respuesta

10

Como complemento a la versión C++ 03, en C++ 11 se obtiene decltype:

template <typename T> 
auto is_proxy(T const&) -> decltype(T::proxy_tag{}, std::true_type{}) { 
    return std::true_type{}; 
} 

std::false_type is_proxy(...) { return std::false_type{}; } 

Y su implementación de Client se convierte en:

template <typename T> 
void Client(T const& t) { 
    ClientHelp(t, is_proxy(t)); 
} 

dulce, no es eso ?

+0

Esto no compila para mí (bastante reciente clang ++ de SVN). El problema parece ser los ** dos ** argumentos para 'decltype()'. ¿Cómo se supone que eso funciona? –

+3

@MichaelWild: utiliza el operador de secuencia ','. Generalmente lo encuentra en condiciones de bucle, como 'para (;; i ++ i, ++ e)', el operador de secuencia evalúa ambos operandos de izquierda a derecha y devuelve el resultado del operando correcto. También se puede encadenar: '++ a, ++ b, ++ c' devolverá el resultado de' ++ c', y se garantiza que el orden de evaluación es '++ a' * y luego' '+ + b' * luego * '++ c'. Aquí, el único uso es tener en la parte izquierda el control SFINAE y, a la derecha, la expresión que arroja el tipo de retorno deseado. Puede intentar envolver todo en otra capa de paréntesis. –

+0

Lo sospechaba tanto (no estaba seguro de cómo estaba haciendo el análisis el compilador aquí). Tal vez necesito agregar paréntesis adicionales? Pero entonces AFAIK, 'decltype' es bastante peculiar cuando se trata de paréntesis ... –

16

Usted puede utilizar el tipo ligero categorización idioma

template<class T, class R = void> 
struct enable_if_type { typedef R type; }; 

template<class T, class Enable = void> 
struct test : std::false_type {}; 

template<class T> 
struct test<T, typename enable_if_type<typename T::is_proxy_tag>::type> : std::true_type 
{}; 

template <typename _Big, typename _Other> 
class CProxy 
{ 
    public: 
    typedef void is_proxy_tag; 
}; 

Así que para hacer una clase de un servidor proxy, sólo tiene que añadir este

typedef void is_proxy_tag; 

y la SFINAE en enable_if_type seleccionará el correcto true_type/false_type especialización

Tenga en cuenta que con boost::mpl::true_ en lugar de std::true_type etc. hace que esta solución funcione para C++ 03.

Cuestiones relacionadas