2012-11-07 13 views
14

Ho dichiarato un boost::variant che accetta tre tipi: string, bool e int. Il seguente codice mostra che la mia variante accetta const char* e la converte in bool. È normale che boost::variant accetti e converta tipi non presenti nel suo elenco?boost :: variant - perché "const char *" è stato convertito in "bool"?

#include <iostream> 
#include "boost/variant/variant.hpp" 
#include "boost/variant/apply_visitor.hpp" 

using namespace std; 
using namespace boost; 

typedef variant<string, bool, int> MyVariant; 

class TestVariant 
    : public boost::static_visitor<> 
{ 
public: 
    void operator()(string &v) const 
    { 
     cout << "type: string -> " << v << endl; 
    } 
    template<typename U> 
    void operator()(U &v)const 
    { 
     cout << "type: other -> " << v << endl; 
    } 
}; 

int main(int argc, char **argv) 
{ 
    MyVariant s1 = "some string"; 
    apply_visitor(TestVariant(), s1); 

    MyVariant s2 = string("some string"); 
    apply_visitor(TestVariant(), s2); 

    return 0; 
} 

uscita:

Tipo: altro -> 1
Tipo: stringa -> qualche stringa

Se rimuovo il tipo bool da MyVariant e modificarla in questo modo:

typedef variant<string, int> MyVariant; 

const char* non è più convertito in bool. Questa volta è convertito in string e questa è la nuova uscita:

Tipo: stringa -> qualche stringa
Tipo: stringa -> qualche stringa

Ciò indica che variant tenta di convertire altri tipi prima a bool e quindi a string. Se la conversione del tipo è qualcosa di inevitabile e dovrebbe sempre accadere, c'è un modo per dare una conversione a string una priorità più alta?

risposta

10

Non penso che questo sia qualcosa di particolarmente da fare con boost::variant, si tratta di quale costruttore viene selezionato dalla risoluzione di sovraccarico. La stessa cosa accade con una funzione di sovraccarico:

#include <iostream> 
#include <string> 

void foo(bool) { 
    std::cout << "bool\n"; 
} 

void foo(std::string) { 
    std::cout << "string\n"; 
} 

int main() { 
    foo("hi"); 
} 

uscita:

bool 

Non so di un modo per cambiare ciò che i costruttori una variante ha [edit: come dice James, è possibile scrivere un'altra classe che utilizza Variant nella sua implementazione. Quindi puoi fornire un costruttore const char* che faccia la cosa giusta.]

Forse potresti cambiare i tipi nella variante. Un altro esempio sovraccarico:

struct MyBool { 
    bool val; 
    explicit MyBool(bool val) : val(val) {} 
}; 

void bar(MyBool) { 
    std::cout << "bool\n"; 
} 

void bar(const std::string &) { 
    std::cout << "string\n"; 
} 

int main() { 
    bar("hi"); 
} 

uscita:

string 

Purtroppo ora devi scrivere bar(MyBool(true)) invece di foo(true). Ancor peggio nel caso della tua variante con string/bool/int, se si cambia semplicemente in una variante di string/MyBool/int allora MyVariant(true) chiamerebbe il costruttore int.

+6

Per completare la tua spiegazione: c'è una conversione implicita da qualsiasi tipo di puntatore a 'bool', e le conversioni implicite incorporate sono sempre scelte in preferenza alle conversioni definite dall'utente. (La conversione 'char *' in 'std :: string' conta come definita dall'utente.) Come per cambiare i costruttori, puoi racchiudere la classe in un'altra classe, o derivare da essa. A seconda del contesto, uno di questi può o non può essere appropriato; entrambi hanno alcuni inconvenienti. –

+0

Penso che una soluzione sia rimuovere 'bool' da' MyVariant' e utilizzare invece i valori 0 e 1. – Meysam

+0

@Meysam: sì. Ho pensato di raccomandarlo, ma poi ho pensato che probabilmente volevi un 'MyVariant' inizializzato con' 0' per essere diverso da un 'MyVariant' inizializzato con' false'. Se è giusto che siano uguali, basta rimuovere 'bool'. Se hanno significati diversi, non è così semplice. –

9

Questo non ha nulla a che fare con boost::variant, ma con l'ordine in cui C++ seleziona le conversioni da applicare. Prima di provare a utilizzare le conversioni definite dall'utente (ricorda che std::string è una classe definita dall'utente per questo scopo), il compilatore proverà le conversioni incorporate.Non v'è alcuna conversione incorporato da const char* a int, ma secondo §4.12 nello standard:

Un prvalue di [...] puntatore [...] tipo può essere convertito in un prvalue di tipo bool.

Così il compilatore converte felicemente tua const char* ad un bool e non viene mai a prendere in considerazione la conversione in un std::string.

+0

Questo mi ha un po 'oggi con una coppia di metodi sovraccaricati, uno che ha preso un 'bool', l'altro un' const std :: string & '. Sfortunato! – davidbak

Problemi correlati