2010-07-20 13 views
10

Ho colpito un vero capocannoniere in C++, non mi è mai successo prima.La funzione modello C++ ottiene valori predefiniti errati

Il nocciolo del problema è che dopo l'invocazione della mia funzione (modello) gli argomenti per i quali sono stati definiti i valori predefiniti hanno i loro valori codificati. Succede solo se chiamo la funzione con i valori predefiniti.

La mia funzione template è dichiarato così:

template <typename T> 
vector2<T> transform(vector2<T> const &vec, matrix4<T> const &m, T z = T(0), T w = T(1)); 

E 'più tardi, nella stessa intestazione, definita in questo modo:

template <typename T> 
inline vector2<T> transform(vector2<T> const &vec, matrix4<T> const &m, T z, T w) 
{ 
vector4<T> res = m * vector4<T>(vec.x, vec.y, z, w); 
return vector2<T>(res.x, res.y); 
} 

Ora, quando io chiamo questo con valori di default (transform(vector2<double>(0, 1), view_transform)) I non ottenere i valori che mi aspetto Entrando in transform con il debugger di VC++ vedo z e w con valori "divertenti" (che nella mia esperienza significa che qualcosa non è inizializzato correttamente).

Esempio valori divertenti sarebbero: ,0078125000000000000 e 2.104431116947e-317 # DEN

Ora ho provato a trovare la risposta su C++ FAQ Lite, googling; ho anche provato a calmarmi con Schubert, ma non posso per la vita di me capirlo. Immagino che sia molto semplice e sospetto che si tratti di una sorta di mascherata da imbroglione al lavoro.

C'è un modo per ottenere i valori predefiniti che mi aspetto e voglio, e perché mi fa questo?

Edit 1:

Se la chiamata cambiamento in modo che utilizza i galleggianti invece (transform(vector2<float>(0, 1), view_transform)) il problema va via. Sembra che ciò accada solo se T = double.

Edit 2:

succede solo se ho due specializzazioni per double e float. Se uso una specializzazione flottante in un punto, la specializzazione doppia ottiene valori predefiniti strani. Se cambio tutti i punti in cui viene chiamata la funzione, quindi usa il doppio, i problemi "vanno via". Non riesco ancora a capire perché, però, è come utilizzare offset difettosi o qualcosa del genere durante la configurazione di z e w.

Edit 3:

Racconti dalla C++ Cripta:

#include <sgt/matrix4.hpp> 

int main(int argc, char *argv[]) 
{ 
    sgt::matrix4<double> m0(
     2, 0, 0, 1, 
     0, 2, 0, 1, 
     0, 0, 1, 0, 
     0, 0, 0, 1); 

    m0 *= m0; 

    sgt::vector2<double> blah0 = sgt::transform(sgt::vector2<double>(1, 0), m0); 

    sgt::matrix4<float> m1(
     2, 0, 0, 1, 
     0, 2, 0, 1, 
     0, 0, 1, 0, 
     0, 0, 0, 1); 

    m1 *= m1; 

    sgt::vector2<float> blah1 = sgt::transform(sgt::vector2<float>(1, 0), m1); 

    printf("%f", blah0.x); 
    printf("%f", blah1.x); 
} 

In matrix4.hpp:

// ... 

template <typename T> 
vector2<T> transform(vector2<T> const &vec, matrix4<T> const &m, T z = T(0), T w = T(1)); 

template <typename T> 
inline vector2<T> transform(vector2<T> const &vec, matrix4<T> const &m, T z, T w) 
{ 
    vector4<T> res = m * vector4<T>(vec.x, vec.y, z, w); 
    return vector2<T>(res.x, res.y); 
} 

// ... 

se corro questo, il doppio di specializzazione ha il difetto argomenti corretti, ma la versione float ottiene sia gli argomenti predefiniti come zero (0.000000) che, anche se meglio, non è ancora z = 0 e w = 1.

Edit 4:

fatto un Connect issue.

+0

Non riesco a vedere niente di sbagliato ... Potrebbe provare con un compilatore diverso? –

+1

Che compilatore stai usando e su quale piattaforma sei? – Alerty

+1

Ah scusa, ho dimenticato questa informazione, Microsoft VC++ 10 (16.00.30319.01). @j_random_hacker: Hmm, beh, potrei scaricare MinGW e provare. – Skurmedel

risposta

5

Di seguito fallisce per il mio in Dev Studio:

#include "stdafx.h" 
#include <vector> 
#include <iostream> 

template <typename T> 
std::vector<std::vector<T> > transform(std::vector<std::vector<T> > const &vec, 
             std::vector<std::vector<std::vector<std::vector<T> > > > const &m, 
             T z = T(0), T w = T(1)); 


template <typename T> 
std::vector<std::vector<T> > transform(std::vector<std::vector<T> > const &vec, 
             std::vector<std::vector<std::vector<std::vector<T> > > > const &m, 
             T z, T w) 
{ 
    std::cout << "Z" << z << "\n"; 
    std::cout << "W" << w << "\n"; 

    return vec; 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    std::vector<std::vector<int> > xi; 
    std::vector<std::vector<std::vector<std::vector<int> > > > mi; 
    transform(xi,mi); 

    std::vector<std::vector<float> > xf; 
    std::vector<std::vector<std::vector<std::vector<float> > > > mf; 
    transform(xf,mf); 

    std::vector<std::vector<double> > xd; 
    std::vector<std::vector<std::vector<std::vector<double> > > > md; 
    transform(xd,md); 
} 

uscita:

Z0 
W1 
Z0 
W1.4013e-045 
Z2.122e-314 
W3.60689e-305 

quindi suppongo che non funziona come previsto !!!

Se si rimuove la pre-dichiarazione e si inseriscono gli argomenti predefiniti nella funzione modello, funzionerà come previsto.

#include "stdafx.h" 
#include <vector> 
#include <iostream> 

template <typename T> 
std::vector<std::vector<T> > transform(std::vector<std::vector<T> > const &vec, 
             std::vector<std::vector<std::vector<std::vector<T> > > > const &m 
             T z = T(0), T w = T(1)) 
{ 
    std::cout << "Z" << z << "\n"; 
    std::cout << "W" << w << "\n"; 

    return vec; 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    std::vector<std::vector<int> > xi; 
    std::vector<std::vector<std::vector<std::vector<int> > > > mi; 
    transform(xi,mi); 

    std::vector<std::vector<float> > xf; 
    std::vector<std::vector<std::vector<std::vector<float> > > > mf; 
    transform(xf,mf); 

    std::vector<std::vector<double> > xd; 
    std::vector<std::vector<std::vector<std::vector<double> > > > md; 
    transform(xd,md); 
} 

Funziona come previsto.
Questo ha qualcosa a che fare con la pre-dichiarazione del modello che in realtà non è una pre-dichiarazione di funzione e quindi in realtà non ha parametri predefiniti e come tale si ottengono valori casuali nell'elenco dei parametri.

OK. Non dalla mia lettura dello standard questo dovrebbe funzionare come previsto:

Utilizzando n2521
Sezione 14.7.1 implicito esemplificazione
Paragrafo 9

Un'implementazione non deve implicitamente un'istanza di un modello di funzione, un modello di membro , una funzione membro non virtuale, una classe membro o un membro dati statici di un modello di classe che non richiede l'istanziazione. Non è specificato se un'implementazione implichi o meno implicitamente una funzione membro virtuale di un modello di classe se la funzione membro virtuale non venga istanziata in altro modo. L'uso di una specializzazione del modello in un argomento predefinito non deve causare l'istanza implicita del modello, eccetto che un modello di classe può essere istanziato dove è necessario il suo tipo completo per determinare la correttezza dell'argomento predefinito. L'utilizzo di un argomento predefinito in una chiamata di funzione fa sì che le specializzazioni nell'argomento predefinito vengano istanziate in modo implicito.

La parte in grassetto del paragrafo sembra (a me) per indicare che ogni specializzazione creato a causa di argomenti di default sarà implicitamente un'istanza nell'unità di traduzione quando viene utilizzato.

Paragrafo 11:

Se un modello funzione f si chiama in un modo che richiede un'espressione argomento di default da utilizzare, i nomi dipendenti sono guardato in su, la semantica vincoli vengono controllati, e l'istanza di qualsiasi modello utilizzato nell'espressione dell'argomento predefinito viene eseguito come se l'espressione dell'argomento predefinito fosse stata un'espressione utilizzata in una specializzazione del modello di funzione con lo stesso ambito, gli stessi parametri del modello e lo stesso accesso a quello del modello di funzione f utilizzato in quel punto . Questa analisi è chiamata istanza di argomento predefinita. L'argomento predefinito istanziato viene quindi utilizzato come argomento di f.

Indica che anche se gli argomenti predefiniti sono parametri del modello, verranno istanziati correttamente.

Beh, spero di averlo interpretato correttamente. :-)

+0

Puoi provare a chiamarlo con due diverse specializzazioni (vedi la mia ultima modifica), come usare sia double che float? Sta iniziando a puzzare come VC++ sta facendo qualcosa di divertente qui. – Skurmedel

+0

Haha oh caro, non ho mai saputo che i valori predefiniti potrebbero essere questo male. – Skurmedel

+0

Qualcuno ha provato questo su g ++/MinGW?Se è illegale C++, sicuramente lo standard C++ imporrà un messaggio di errore del compilatore ...? –

1

Non so se funzionerà, ma provare a utilizzare un static_cast anziché un cast di stile C per i valori predefiniti.

* Modifica: Apparentemente, il problema è il compilatore.

+0

Restituisce tristemente gli stessi risultati. – Skurmedel

+3

Tecnicamente non si trattava di un cast in stile C ma di una chiamata al costruttore parametrizzata. –

+0

@ Hall: non è una chiamata del costruttore. – Alerty

2

Il codice è ottimizzato? Forse è per questo che il debugger ti mostra i valori sbagliati.

Ho provato questo codice più semplice (in g ++ 4.3.3) e funziona come previsto.

template <typename T> 
T increment(T a, T b = T(1)) 
{ 
    return a + b; 
} 

int main() 
{ 
    double a = 5.0; 
    std::cout << increment(a) << ", "; 
    std::cout << increment(a, 3.0) << "\n"; 
} 
+0

Mostra lo stesso comportamento sia in release che in debug (ottimizzazione disattivata), ma verificando se ha fatto la differenza ho scoperto una cosa strana. Succede solo se T è doppio, non se è float ?! – Skurmedel