2010-10-08 14 views
6

L'ultima domanda che ho posto è stata qualcosa su cui mi sono imbattuto quando ho cercato di capire un'altra cosa ... che anch'io non riesco a capire (non è il mio giorno).Conversione implicita non avvenuta

Questa è una domanda abbastanza lunga, ma almeno spero che questa domanda possa rivelarsi utile a molte persone e non solo a me.

Il codice che ho è il seguente:

template <typename T> class V; 
template <typename T> class S; 

template <typename T> 
class V 
{ 
public: 
T x; 

explicit V(const T & _x) 
:x(_x){} 

V(const S<T> & s) 
:x(s.x){} 
}; 

template <typename T> 
class S 
{ 
public: 
T &x; 

explicit S(V<T> & v) 
:x(v.x) 
{} 
}; 

template <typename T> 
V<T> operator+(const V<T> & a, const V<T> & b) 
{ 
return V<T>(a.x + b.x); 
} 

int main() 
{ 
V<float> a(1); 
V<float> b(2); 
S<float> c(b); 

b = a + V<float>(c); // 1 -- compiles 
b = a + c;   // 2 -- fails 
b = c;    // 3 -- compiles 

return 0; 
} 

Espressioni 1 e 3 funzionano perfettamente, mentre l'espressione 2 non viene compilato.

Se ho capito correttamente, ciò che accade è:

Espressione 1

  1. c si è implicitamente convertito const utilizzando una sequenza di conversione standard (composto su un solo qualificazione conversione).
  2. V<float>(const S<T> & s) viene chiamato e un temporale l'oggetto const V<float> generato (chiamiamolo t). È già const-qualificato perché è un valore temporale.
  3. a viene convertito in const analogamente a c.
  4. operator+(const V<float> & a, const V<float> & b) viene chiamato, risultante in un tipo temporale const V<float> che possiamo chiamare q.
  5. viene chiamato il valore predefinito V<float>::operator=(const & V<float>).

Sono OK fino a qui? Se ho fatto anche il più sottile errore per favore, fatemelo sapere, perché io sto cercando di acquisire una comprensione del casting implicito il più profondamente possibile ...

Expression 3

  1. c è convertito in V<float>. Per questo, abbiamo una sequenza di conversione definita dall'utente:
    1.1. prima conversione standard: S<float> a const S<float> tramite conversione di qualifica.
    1.2. conversione definita dall'utente: const S<float> a V<float> tramite il costruttore V<float>(const S<T> & s).
    conversione secondo standard 1,3: V<float> a const V<float> tramite conversione di qualifica.
  2. viene chiamato il valore predefinito V<float>::operator=(const & V<float>).

Espressione 2?

Quello che non capisco è perché c'è un problema con la seconda espressione. Perché la seguente sequenza non è possibile?

  1. c viene convertito in V<float>. Per questo, abbiamo una sequenza di conversione definita dall'utente:
    1.1. conversione standard iniziale: S<float> a const S<float> tramite conversione di qualifica.
    1.2. conversione definita dall'utente: const S<float> a V<float> tramite il costruttore V<float>(const S<T> & s).
    1.3. conversione standard finale: V<float> a const V<float> tramite conversione di qualifica.
  2. passaggi da 2 a 6 sono le stesse in caso di espressione 1.

Dopo aver letto il C++ standard di ho pensato: 'hey! forse il problema deve essere risolto con 13.3.3.1.2.3! ' in cui si afferma:

If the user-defined conversion is specified by a template conversion function, the second standard conversion sequence must have exact match rank.

Ma questo non può essere il caso, in quanto la conversione di qualificazione ha esatto rango partita ...

ho davvero idea ...

Beh, se hai la risposta o meno, grazie per aver letto fino a qui :)

risposta

9

Come sottolineato da Edric, le conversioni non vengono considerate durante la deduzione degli argomenti del modello. Qui, ci sono due contesti in cui il parametro di modello T può essere dedotta dal tipo degli argomenti:

template<class T> 
v<T> operator+(V<T> const&, V<T> const&); 
       ~~~~~~~~~~~ ~~~~~~~~~~~~ 

Ma si tenta di richiamare questo modello di funzione con una V<float> sul lato sinistro e una S sul lato destro. La deduzione dell'argomento modello risulta in T = float per il lato sinistro e verrà visualizzato un errore per il lato destro perché non esiste T in modo che V<T> sia uguale a S<T>. Ciò si qualifica come errore di deduzione degli argomenti del modello e il modello viene semplicemente ignorato.

Se si desidera consentire le conversioni, l'operatore + non deve essere un modello. V'è il seguente trucco: è possibile definire come un amico in linea all'interno del modello di classe per V:

template<class T> 
class V 
{ 
public: 
    V(); 
    V(S<T> const&); // <-- note: no explicit keyword here 

    friend V<T> operator+(V<T> const& lhs, V<T> const& rhs) { 
     ... 
    } 
}; 

In questo modo, l'operatore non è più un modello.Quindi, non c'è bisogno di dedurre argomenti template e la tua invocazione dovrebbe funzionare. L'operatore viene trovato tramite ADL (ricerca dipendente dall'argomento) perché il lato sinistro è un V<float>. Il lato destro viene convertito correttamente in un V<float>.

È inoltre possibile disabilitare la deduzione degli argomenti del modello per un argomento specifico. Per esempio:

template<class T> 
struct id {typedef T type;}; 

template<class T> 
T clip(
    typename id<T>::type min, 
    T value, 
    typename id<T>::type max) 
{ 
    if (value<min) value=min; 
    if (value>max) value=max; 
    return value; 
} 

int main() { 
    double x = 3.14; 
    double y = clip(1,x,3); // works, T=double 
} 

Anche se il tipo del primo e l'ultimo argomento è un int, essi non sono considerati nel modello detrazione argomento perché id<T>::type non è un cosiddetto context` * deducibili. Quindi, T è dedotta solo in base al secondo argomento, che risulta in T = double senza contraddizioni.

+0

Grazie mille !! Stavo guardando il problema tutto sbagliato dall'inizio ... grazie anche per il trucco dell'amico operatore, penso sia quello che farò :) – jmeseguerdepaz

+0

grazie, buono. ancora un'altra prova che C++ può essere davvero brutto e cattivo! – Atmocreations

+0

Puoi usare 'std :: identity' in' 'invece di' id' – David

0

Solo una supposizione, ma forse il compilatore non può distinguere tra la conversione da V-> S o da S-> V mentre si prova a capire come aggiungere un + c nell'espressione 2. Si presume che il compilatore sia intelligente ugh per scegliere quello che consente alla compilazione di procedere per il resto delle funzioni disponibili, ma probabilmente il compilatore non sta "leggendo in anticipo" (per così dire), e si sta confondendo con l'ambiguità della up-conversion prima di provare per trovare l'operatore '+'.

Naturalmente, se si aggiunge l'errore di compilazione, potrebbe contribuire a chiarire il problema troppo ...

+0

Bene, secondo lo standard l'operatore + è una funzione candidata perché ha il numero corretto di argomenti. Quindi il compilatore dovrebbe essere in grado di tracciare la conversione che ho appena scritto ... o così ho capito. Per quanto riguarda il messaggio di errore: ../main.cpp:42: errore: nessuna corrispondenza per 'operator +' in 'a + c' (mi dispiace, non so come inserire qui nuove righe) – jmeseguerdepaz

+0

Come ho detto, che richiederebbe al compilatore di "leggere in anticipo" e convertire c utilizzando il costruttore V, prima che fosse in grado di far corrispondere l'operatore personalizzato + specificato. Dato che non esiste un operatore di corrispondenza diretta e nessuna conversione up definitiva (che potrebbe essere necessariamente intrinseca, non è sicura), il compilatore si arrende. Questa è la mia ipotesi sulla logica del compilatore, comunque. – Nick

+0

Precisazione: non si dispone di un operatore candidato, poiché il proprio operatore non può essere istanziato da un modello per abbinare i tipi aggiunti senza richiamare operatori di conversione personalizzati. Questa è la mia comprensione. – Nick

4

Quando si considera partite template, le conversioni implicite non vengono utilizzati. Pertanto, nel seguente semplice esempio:

template < typename T > 
void foo(T t1, T t2) { /* do stuff */ } 

int main(int argc, char ** argv) { 
    foo(1, 1.0); 
    return 0; 
} 

Questo sarà non compilare anche se uno degli argomenti potrebbe essere convertito in modo implicito l'altro tipo (int < -> doppia).

+0

Hai ragione, il codice fornito non funziona. Ma perché? Non riesco a vedere un motivo sullo standard, e in realtà http://accu.org/index.php/articles/268 dice "n la maggior parte dei casi un modello di funzione si comporta come una normale funzione quando si considera la risoluzione di sovraccarico". E infine abbiamo la cosa che la conversione implicita avviene nell'espressione "b = c" – jmeseguerdepaz

+0

A proposito, il motivo per cui il tuo codice non funziona è un problema di istanziazione, non di conversione implicita. Se chiami "foo" con foo (1, 1.0) non ci sono problemi nella compilazione. – jmeseguerdepaz

+0

Oh, dopo aver letto la risposta di sellibitze capisco che probabilmente stavi cercando di dirmi solo che ... non ho capito, mi dispiace. – jmeseguerdepaz

Problemi correlati