2013-06-03 16 views
11

Come determinare (nello spirito <type_traits>) o meno esplicitamente un tipo convertibile in un altro tipo? Per esempio, io voglio controllare la presenza di F::explicit operator double const &() const; per qualche class/structF, ma, allo stesso tempo, F non dovrebbe stati esplicitamente convertibile in float o long double (qualcosa come pred< double const & >::value && !pred<float>::value && !pred< long double >::value).controllo se tipo può essere convertito in modo esplicito

notare che std::is_convertible< From, To >::value controlli "se Da possono essere convertiti in Per utilizzando conversione implicita". Ma desidero determinare se esiste l'operatore di conversione esplicita .

E, se possibile, il "come determinare, tipo Da è convertibile in un riferimento cioè digitare Per?"?

+0

forse sono fraintendimento della domanda, ma si può fare un 'dynamic_cast (FROM & val)', si getterà 'std :: bad_cast' se la conversione non è ammissibile. –

+0

@ H2CO3 Si supponga, tale questione è di circa compilazione problema di tempo. – Orient

+2

come si vuole verificare la presenza di un operatore conversione esplicita, il vostro compito sembra ridursi a [determinare se una classe T ha una funzione di membro con una data di firma] (http://stackoverflow.com/q/87372/1362568). Ci sono molte risposte là, tra cui uno dei miei. –

risposta

11

è necessario definire il proprio:

template <class U, class T> 
struct is_explicitly_convertible 
{  
    enum {value = std::is_constructible<T, U>::value && !std::is_convertible<U, T>::value}; 
}; 
7

L'apparecchio di prova è a un collegamento di off-site ed è variato dal tuo post originale, così ho copierà testualmente il test-set di cui sto parlando qui:

static_assert(is_explicitly_convertible< double, double >::value, "1"); 
static_assert(is_explicitly_convertible< double &, double >::value, "2"); 
static_assert(is_explicitly_convertible< double const, double >::value, "3"); 
static_assert(is_explicitly_convertible< double const &, double >::value, "4"); 

static_assert(is_explicitly_convertible< double, double const & >::value, "5"); 
static_assert(is_explicitly_convertible< double &, double const & >::value, "6"); 
static_assert(is_explicitly_convertible< double const, double const & >::value, "7"); 
static_assert(is_explicitly_convertible< double const &, double const & >::value, "8"); 

static_assert(!is_explicitly_convertible< double, double & >::value, "9"); // not a ref 
static_assert(is_explicitly_convertible< double &, double & >::value, "10"); 
static_assert(!is_explicitly_convertible< double const, double & >::value, "11"); 
static_assert(!is_explicitly_convertible< double const &, double & >::value, "12"); 

static_assert(is_explicitly_convertible< double, double const >::value, "13"); 
static_assert(is_explicitly_convertible< double &, double const >::value, "14"); 
static_assert(is_explicitly_convertible< double const, double const >::value, "15"); 
static_assert(is_explicitly_convertible< double const &, double const >::value, "16"); 

static_assert(is_explicitly_convertible< AA const &, A const & >::value, "=&1.a"); 
static_assert(is_explicitly_convertible< CC const &, C const & >::value, "=&1.b"); 
static_assert(is_explicitly_convertible< BB const &, B const & >::value, "=&1.c"); 
static_assert(!is_explicitly_convertible< AA const &, A & >::value, "&1.a"); 
static_assert(!is_explicitly_convertible< CC const &, C & >::value, "&1.b"); 
static_assert(!is_explicitly_convertible< BB const &, B & >::value, "&1.c"); 

static_assert(is_explicitly_convertible< AA const, A const & >::value, "=1.a"); 
static_assert(is_explicitly_convertible< CC const, C const & >::value, "=1.b"); 
static_assert(is_explicitly_convertible< BB const, B const & >::value, "=1.c"); 
//static_assert(!is_explicitly_convertible< AA const, A >::value, "=2.a"); // ??????????????? 
//static_assert(!is_explicitly_convertible< CC const, C >::value, "=2.b"); 
//static_assert(!is_explicitly_convertible< BB const, B >::value, "=2.c"); 
static_assert(!is_explicitly_convertible< AA const, A & >::value, "=3.a"); // good! 
static_assert(!is_explicitly_convertible< CC const, C & >::value, "=3.b"); // 
static_assert(!is_explicitly_convertible< BB const, B & >::value, "=3.c"); // 
static_assert(!is_explicitly_convertible< AA const, A && >::value, "=4.a"); // not interesting 
static_assert(!is_explicitly_convertible< CC const, C && >::value, "=4.b"); // 
static_assert(!is_explicitly_convertible< BB const, B && >::value, "=4.c"); // 
static_assert(!is_explicitly_convertible< AA const, B const & >::value, "=5.a"); 
static_assert(!is_explicitly_convertible< AA const, C const & >::value, "=5.b"); 
static_assert(!is_explicitly_convertible< BB const, A const & >::value, "=5.c"); 
static_assert(!is_explicitly_convertible< BB const, C const & >::value, "=6.a"); 
static_assert(!is_explicitly_convertible< CC const, A const & >::value, "=6.b"); 
static_assert(!is_explicitly_convertible< CC const, B const & >::value, "=6.c"); 
static_assert(!is_explicitly_convertible< AA const, B & >::value, "=7.a"); 
static_assert(!is_explicitly_convertible< AA const, C & >::value, "=7.b"); 
static_assert(!is_explicitly_convertible< BB const, A & >::value, "=7.c"); 
static_assert(!is_explicitly_convertible< BB const, C & >::value, "=8.a"); 
static_assert(!is_explicitly_convertible< CC const, A & >::value, "=8.b"); 
static_assert(!is_explicitly_convertible< CC const, B & >::value, "=8.c"); 
static_assert(!is_explicitly_convertible< AA const, B >::value, "=9.a"); // very subtle moment (see class AA above) 
static_assert(!is_explicitly_convertible< AA const, C >::value, "=9.b"); 
static_assert(is_explicitly_convertible< BB const, A >::value == std::is_constructible< A, A && >::value, "=9.c"); // (see class BB above) 
static_assert(!is_explicitly_convertible< BB const, C >::value, "=10.a"); 
static_assert(!is_explicitly_convertible< CC const, A >::value, "=10.b"); 
static_assert(!is_explicitly_convertible< CC const, B >::value, "=10.c"); 

static_assert(is_explicitly_convertible< AA, A & >::value, "~1.a"); 
static_assert(is_explicitly_convertible< CC, C & >::value, "~1.b"); 
static_assert(is_explicitly_convertible< BB, B & >::value, "~1.c"); 
//static_assert(!is_explicitly_convertible< AA, A >::value, "~2.a"); // ??????????????? 
//static_assert(!is_explicitly_convertible< CC, C >::value, "~2.b"); 
//static_assert(!is_explicitly_convertible< BB, B >::value, "~2.c"); 
static_assert(is_explicitly_convertible< AA, A const & >::value, "~3.a"); // convertible 
static_assert(is_explicitly_convertible< CC, C const & >::value, "~3.b"); // 
static_assert(is_explicitly_convertible< BB, B const & >::value, "~3.c"); // 
static_assert(!is_explicitly_convertible< AA, B const & >::value, "~4.a"); 
static_assert(!is_explicitly_convertible< AA, C const & >::value, "~4.b"); 
static_assert(!is_explicitly_convertible< BB, A const & >::value, "~4.c"); 
static_assert(!is_explicitly_convertible< BB, C const & >::value, "~5.a"); 
static_assert(!is_explicitly_convertible< CC, A const & >::value, "~5.b"); 
static_assert(!is_explicitly_convertible< CC, B const & >::value, "~5.c"); 

static_assert(std::is_convertible< double, double const & >::value, "5*"); 
static_assert(!std::is_convertible< double, double & >::value, "9*"); 

Se usufruire di gcc 4.7.2 (e forse anche prima, non ho controllato), allora il C++ 11 libreria standard risolve il problema:

std::is_explicitly_convertible<From,To> 

è definito in <type_traits>

Tuttavia, si sarebbe poi essere avvalendosi di un error in quella versione del C++ Standard Library 11. Questo modello di tratto non avrebbe dovuto essere lì, perché è stato rimosso dallo standard in base a N3047 (2010).

Se siete a GCC 4.8.1 (o forse 4.8, non ho controllato), allora questo tratto non è più nella biblioteca, e se lo vuoi devi ancora rotolare il proprio.

Ma sarebbe solo umano per ispezionare la definizione di gcc 4.7.2 di <type_traits> per un inizio, e facendo che rivela che il realizzatore GNU considerato il tratto di essere altro che l'inverso di std::is_constructible<To,From>:

/// is_explicitly_convertible 
template<typename _From, typename _To> 
struct is_explicitly_convertible 
: public is_constructible<_To, _From> 
{ }; 

Si può anche pensare: Ma naturalmente.

Allora, perché non sarà che fare per essere in corso con? N3047 spiega:

La domanda rimanente è, in che modo il tratto interessato anche is_explicitly_convertible deve essere riparato.Le scelte di base sono:

  1. Fix is_explicitly_convertible restituendo l'espressione static_cast corrente, non è più rendendo is_explicitly_convertible dipende is_constructible.
  2. Rimuovere is_explicitly_convertible dallo standard.

La prima scelta è stata presa in considerazione, ma si è scoperto che esiste una comprensione abbastanza diversa di ciò che "esplicitamente convertibile" dovrebbe in realtà significare. Mentre alcuni ritengono che static_cast esprima correttamente questo, altri hanno creduto che il is_constructible fisso fornisse anche un significato migliore per is_explicitly_convertible. Pertanto, questo documento consiglia di rimuovere is_explicitly_convertible dalla bozza di lavoro. Questo non dovrebbe essere pericoloso ora, perché nulla dipende ancora da quella definizione speciale. E se risultasse che il tratto sarebbe comunque utile, potrebbe essere aggiunto in un'altra revisione dello standard.

non c'era bug noto in tale definizione, ma non vi erano viste opposte sul fatto che il significato di "esplicitamente convertibile" che codifica è quella giusta: -

  • D1) From è esplicitamente convertibile To = df To è costruibile da From
  • D2) From è esplicitamente convertibile in To = df From può essere gettato in modo statico a To

Non voglio discutere di questa polemica (non ho ancora elaborato quale sia la differenza, io stesso) ma vi suggerisco che basta pagate i vostri soldi e fare la vostra scelta.

Se si preferisce D1), si può semplicemente prendere la definizione del tratto da gcc 4.7.2 <type_traits>, come sopra.

Se sei a favore D2, allora si potrebbe plagiare e adattare la definizione di std::is_convertible<From,To> da gcc 4.8.1 <type_traits>. Questa definizione richiama le funzioni ausiliarie interne da rintracciare.L'adattamento si vorrebbe sarebbe quello di cambiare il test SFINAE per From può essere implicitamente in To ad un test per From può essere static_cast a To; e ciò significherebbe sostituzione:

template<typename _From1, typename _To1> 
    static decltype(__test_aux<_To1>(std::declval<_From1>()), __one()) 
__test(int); 

con:

template<typename _From1, typename _To1> 
    static decltype(__test_aux<_To1>(static_cast<_To1>(std::declval<_From1>())), __one()) 
__test(int); 

Una versione ridotta di questa definizione (ignorando questi casi come From essere void e To essere un tipo di funzione o array) che farebbe per i tipi che vengono testati nelle vostre static_assert s:

template<typename From, typename To> 
struct is_explicitly_convertible 
{ 
    template<typename T> 
    static void f(T); 

    template<typename F, typename T> 
    static constexpr auto test(int) -> 
    decltype(f(static_cast<T>(std::declval<F>())),true) { 
     return true; 
    } 

    template<typename F, typename T> 
    static constexpr auto test(...) -> bool { 
     return false; 
    } 

    static bool const value = test<From,To>(0); 
}; 

Sia la definizione D1 o il cut-down definizione D2 sostiene tutte 63 le tue static_assert s. (I compilata con g ++ 4.7.2 e 4.8.1, -g;-O0;-Wall;-std=c++11, e anche con il -std=gnu++1y che un lavoratore dipendente)

Il tuo più recente soluzione dimostra che hai fatto la tua strada per la scuola D2, con un diverso stile di implementazione.

Dei due, finché non trovo qualcosa che non va, io preferirei la definizione D1, come da gcc 4.7.2, solo perché è molto più semplice e, in particolare, è un derivato banale di std::is_constructible.

+1

Sia D1) e D2) approcci sono perfettamente equivalenti sul mio banco di prova lo scorso __gcc__ (mingw-build 4.8.1 rev0) con '-std = gnu 1y' opzione ++. Quindi, alla fine, scelgo D1), dal momento che non mi richiede di scrivere nulla di nuovo. – Orient

Problemi correlati