2011-12-13 30 views
5

En C++, quiero tener una clase cuyos constructores son los siguientes:¿Cómo comprobar si un tipo es un typedef int

class A { 
    explicit A(A* other) { ... } 
    explicit A(intptr_t other) { ... } 
}; 

El problema con esto es que si el usuario se inicializa con

A a(0); 

Luego, en un sistema de 64 bits, el compilador se quejará de que no sabe si 0 se debe convertir a o al intptr_t, lo cual es suficiente. Como quiero que esta notación simple para trabajar, he añadido el siguiente constructor:

explicit A(int a) { assert(a==0); ... } 

La afirmación se debe a que este es el único número entero que esto tiene sentido para. Ahora, el problema surge con un sistema de 32 bits, en el que intptr_t es en realidad ... int! Entonces, ahora, el sistema se queja de que hay dos constructores que toman el mismo tipo de parámetro (que, una vez más, es lo suficientemente justo).

Así que mi pregunta es: ¿hay alguna manera con el preprocesador para detectar que intptr_t es en realidad int y, en ese caso, no compila el constructor con int. O bien, ¿existe otra forma de hacer que la notación A a(0) sea válida sin agregar el constructor con int, pero sin eliminar ninguno de los dos primeros constructores (y tampoco implicándolos)?

+0

No podría eliminar el constructor A (A * other) y reemplazarlo por A (A y otro). Me parece más natural (suponiendo que está copiando otro y no encadenando). –

Respuesta

3

Algo así como

#if INTPTR_MAX == INT_MAX 

puede hacer el truco, pero todavía dará lugar a cierto cuando long y int son del mismo tamaño, y ptrint_t es un typedef para long. Otra posibilidad (pero no sé si puede usarla o no) sería usar uintptr_t, en lugar de intptr_t.

Más allá de estos: el preprocesador no conoce los tipos, por lo que el problema no se puede resolver allí. Tendrá que utilizar algún truco de meta-programación : crea una plantilla para el constructor int, usando boost::enable_if para activarlo solo si el argumento tiene el tipo int. Si ptrint_t es int, entonces la función activada nunca será utilizada, porque nunca será una mejor coincidencia que la función sin plantilla con la misma firma. Si ptrint_t no es int, entonces la instanciación de la plantilla será una mejor coincidencia cuando el argumento tenga el tipo int. (Tenga en cuenta que nunca he probado esto mismo:. Me suena como que debería ser posible, pero no estoy tan familiarizado con boost::enable_if a estar seguro)

+0

¡Gracias, al final, usando 'uintptr_t' resuelto por el problema! – PierreBdR

+0

@PierreBdR Las soluciones más simples son las mejores :-)! –

1

¿Por qué no implemment sencilla un sin parámetros constructor que actúa como si other fuera 0?Si, por alguna razón usted no desea, se sugiere emplear caracteres de tipo, siempre que tenga acceso a C++ 11 compilador o impulso:

class A { 
public: 
    explicit A(A* other) { ... } 
    explicit A(intptr_t other) { ... } 

    template <class T> 
    explicit A(T other) 
    { 
     static_assert(std::is_convertible<T, intptr_t>::value, "Could not convert value to intptr_t"); 
     static_assert(std::is_integral<T>::value, "Argument must be integral"); 
     intptr_t p = other; 
     ... 
    } 
}; 

Usted puede deshacerse de las afirmaciones de tipo estáticas y comprobaciones, pero entonces en vez de error del compilador se obtendría una advertencia (dependiendo de su nivel de advertencia, incluso puede ser convertido en un error) cuando hace lo siguiente:

A a(0.0f); 
+0

Es posible, pero tiene dos inconvenientes: es complicado, cualquier uso del constructor con un tipo incorrecto dará un error no obvio. – PierreBdR

+0

@PierreBdR ¿Qué es lo que te parece complicado aquí? Las aserciones estáticas son auto explicativas. Lo mismo para los mensajes de error generados por ellos. ¿El "argumento debe ser integral" realmente no es obvio? – gwiazdorrr

+0

Es más un problema de usar una función de plantilla para resolver un problema no vinculado a las plantillas en primer lugar. De todos modos, como puede ver, mi solución preferida ha sido cambiar de 'intptr_t' a' uintptr_t', que es, lo admitirá, más simple. – PierreBdR

-1

¿hay otra manera de hacer que la notación A a(0) válida

Simplemente introduzca un constructor template.

class A { 
public: 
    template<typename T> 
    explicit A (T t) { assert(t==0); } // explicit matters ? 

    explicit A(A* other) { ... } 
    explicit A(intptr_t other) { ... } 
}; 

¡Esto resolverá sus problemas de 32 bits y 64 bits!

+1

¿Y cómo resuelve eso el problema? ¿Error de acceso en lugar de error de ambigüedad? – gwiazdorrr

+0

@gwiazdorrr, estaba modificando la publicación. Ver la respuesta actualizada. Pensé que OP quería restringir '0'. Sin embargo, en cuestión se menciona como 'assert()', lo que significa lo contrario. Lo corregí. – iammilind

+0

¿Por qué un voto a favor? – iammilind

-1

También puede pasar un parámetro que determina cuál se llama:

struct TakePtr{}; 
struct TakeInt{}; 

class A { 
    A(A* other, const TakePtr&) { ... } 
    A(intptr_t other, const TakeInt&) { ... } 
}; 

De esta manera usted puede asegurarse de la cual se llama al constructor:

A a2(0, TakeInt());  // calls the 2nd constructor, taking int 
A a1(&a2, TakePtr()); // calls the 1st constructor, taking a pointer 
+1

Esta no es una buena manera de lograr la tarea. Introduce el [olor del código de lectura] innecesario (http://en.wikipedia.org/wiki/Code_smell#Common_code_smells). Se puede hacer de una manera más simple. – iammilind

+0

@iammilind Entonces, ¿cuál elemento exactamente de esa lista es el código de ruptura anterior? ¿O es más artículos? El código anterior evitará cualquier conversión, y está usando el constructor específico para crear un objeto. –

+0

'O es más artículos'; sí, eso es innecesario. Su código cumplirá la tarea, pero al mismo tiempo, el codificador debe ser disciplinado para pasar el tipo apropiado; por lo tanto, pierde su abstracción. Además, este tipo de error puede pasar desapercibido en silencio: 'A a2 (0, TakePtr()); // oops debería ser TakeInt() ' – iammilind

0

supongo que lo más sencillo es a declare los 6 constructores (int, long, long long y sus variantes sin firmar), en lugar de usar intptr_t.

+0

Entonces, ¿por qué no tener una solución 'plantilla' aún más simple? – iammilind

+2

Creo que esto es exagerado ... – PierreBdR

+0

Esta es una solución simple y efectiva para el problema que usted indicó –

Cuestiones relacionadas