2011-12-13 18 views
5

In C++, voglio avere una classe i cui costruttori sono i seguenti:Come verificare se un tipo è un typedef di int

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

Il problema è se l'utente inizializza con

A a(0); 

Quindi, su un sistema a 64 bit, il compilatore si lamenterà che non sa se 0 deve essere convertito in A* o intptr_t, che è abbastanza giusto. Come voglio che questo semplice notazione per lavorare, ho aggiunto il seguente costruttore:

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

L'asserzione è perché questo è l'unico intero questo ha un senso per. Ora, il problema sorge con un sistema a 32 bit, in cui intptr_t è in realtà ... int! Così ora, il sistema lamenta che ci sono due costruttori che assumono lo stesso tipo di parametro (che, di nuovo, è abbastanza giusto).

Quindi la mia domanda è: esiste un modo con il preprocessore per rilevare che è in realtà intptr_tint e, in quel caso, non compilare il costruttore con int. Oppure, esiste un altro modo per rendere valida la notazione A a(0) senza aggiungere la funzione di costruzione con int, ma senza rimuovere nessuno dei due primi costruttori (e non renderli impliciti neanche).

+0

Impossibile rimuovere il costruttore A (A * altro) e sostituirlo con A (A e altro). Sembrerebbe più naturale per me (supponi che stai copiando altro e non concatenando). –

risposta

3

Qualcosa di simile

#if INTPTR_MAX == INT_MAX 

potrebbe fare il trucco, ma sarà ancora tradurrà in vera dove long e int sono della stessa dimensione, e ptrint_t è un typedef per long. Un'altra possibilità (ma non so se è possibile utilizzarla o meno) sarebbe utilizzare uintptr_t, anziché intptr_t.

Oltre questi: il preprocessore non conosce i tipi, quindi il problema non può essere risolto lì. Dovrai utilizzare una sorta di meta-programmazione trucco: si rende il costruttore int un modello, utilizzando boost::enable_if per attivarlo solo se l'argomento ha tipo int. Se ptrint_t è int, la funzione attivata non sarà mai utilizzata, perché non sarà mai una corrispondenza migliore della funzione non modello con la stessa firma. Se ptrint_t non è int, l'istanziazione del modello sarà una corrispondenza migliore quando l'argomento ha tipo int. (Si noti che non ho mai provato io stesso:. Suona per me come dovrebbe essere possibile, ma io non sono che la familiarità con boost::enable_if per essere sicuri)

+0

Grazie, alla fine, usando 'uintptr_t' risolto dal problema! – PierreBdR

+0

@PierreBdR Le soluzioni più semplici sono le migliori :-)! –

1

Perché non semplice implemment un senza parametri costruttore che funziona come se other fosse 0?Se, per qualche motivo non si vuole, io suggerisco di usare caratteri morfologici, purché si disponga di accesso a C++ 11 compilatore o spinta:

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; 
     ... 
    } 
}; 

È possibile liberarsi di asserzioni statiche e di tipo verifiche, ma allora invece di errore di compilazione si ottiene un messaggio di avviso (a seconda del vostro livello di avviso, può anche essere trasformato in un errore) quando si effettuano le seguenti operazioni:

A a(0.0f); 
+0

È possibile, ma ha due aspetti negativi: è complicato, qualsiasi utilizzo del costruttore con un tipo sbagliato darà un errore non ovvio. – PierreBdR

+0

@PierreBdR Che cosa trovi esattamente complicato qui? Le asserzioni statiche sono auto-esplicative. Lo stesso per i messaggi di errore generati da loro. "L'argomento deve essere integrale" è davvero non ovvio? – gwiazdorrr

+0

È più un problema nell'utilizzo di una funzione modello per risolvere un problema non collegato ai modelli in primo luogo. Comunque, come puoi vedere, la mia soluzione preferita è stata quella di passare da 'intptr_t' a' uintptr_t', che è, lo ammetterete, più semplice. – PierreBdR

-1

c'è un altro modo per rendere la notazione A a(0) valida

Basta introdurre un costruttore 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) { ... } 
}; 

Questo risolverà il tuo problema a 32 e 64 bit!

+1

E come risolve il problema? Errore di accesso invece di errore di ambiguità? – gwiazdorrr

+0

@gwiazdorrr, stavo modificando il post. Vedi la risposta aggiornata. Pensavo che OP volesse limitare '0'. Comunque in questione è menzionato come 'assert()', che significa l'altro modo. L'ho corretto – iammilind

+0

Perché un downvote? – iammilind

-1

È inoltre possibile passare un parametro che determina quale è chiamato:

struct TakePtr{}; 
struct TakeInt{}; 

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

In questo modo è possibile assicurarsi che il costruttore si chiama:

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

Questo non è un buon modo per eseguire l'operazione. Introduce l'inutile [readability code smell] (http://en.wikipedia.org/wiki/Code_smell#Common_code_smells). Può essere fatto in modo più semplice. – iammilind

+0

@iammilind Quindi, quale elemento esattamente da quella lista è il suddetto codice di rottura? O è più oggetti? Il codice precedente impedirà qualsiasi conversione e utilizza il costruttore specifico per creare un oggetto. –

+0

'O è più oggetti? sì, non è necessario. Il tuo codice eseguirà il compito, ma allo stesso tempo, il codificatore deve essere disciplinato su come passare il tipo appropriato; quindi perde la sua astrazione. Inoltre, questo tipo di bug può passare inosservato in silenzio: 'A a2 (0, TakePtr()); // oops dovrebbe essere TakeInt() ' – iammilind

0

Credo che la cosa più semplice è quello di dichiarare tutti i 6 costruttori (int, long, long long e le loro varianti senza segno), invece di usare intptr_t.

+0

Allora perché non avere ancora la soluzione 'template' più semplice? – iammilind

+2

Penso che questo sia eccessivo ... – PierreBdR

+0

Questa è una soluzione semplice ed efficace al problema che hai dichiarato –

Problemi correlati