2012-12-09 6 views
8

Sto scrivendo una classe vettoriale semplice e vorrei avere alcune funzioni membro che sono disponibili solo in vettori di determinate lunghezze (prodotto incrociato per un vettore di 3 elementi per esempio). Mi sono imbattuto in std :: enable_if e sembra che sia in grado di fare ciò che voglio, ma non riesco a farlo funzionare correttamente.Utilizzo di C++ 11 std :: enable_if per abilitare la funzione membro se il vettore è lunghezza specifica

#include <iostream> 
#include <type_traits> 

template<typename T, unsigned int L> 
class Vector 
{ 
    private: 
     T data[L]; 

    public: 
     Vector<T,L>(void) 
     { 
      for(unsigned int i = 0; i < L; i++) 
      { 
       data[i] = 0; 
      } 
     } 

     T operator()(const unsigned int i) const 
     { 
      return data[i]; 
     } 

     T& operator()(const unsigned int i) 
     { 
      return data[i]; 
     } 

     Vector<typename std::enable_if<L==3, T>::type, L> cross(const Vector<T,L>& vec2) const 
     { 
      Vector<T,L> result; 

      result(0) = (*this)(1) * vec2(2) - (*this)(2) * vec2(1); 
      result(1) = (*this)(2) * vec2(0) - (*this)(0) * vec2(2); 
      result(2) = (*this)(0) * vec2(1) - (*this)(1) * vec2(0); 

      return result; 
     } 
}; 

int main(void) 
{ 
    Vector<double,3> v1; 
    Vector<double,3> v2; 
    Vector<double,3> v3; 
    //Vector<double,4> v4; 

    v1(0) = 1; 
    v1(1) = 2; 
    v1(2) = 3; 

    v2(0) = 4; 
    v2(1) = 5; 
    v2(2) = 6; 

    v3 = v1.cross(v2); 

    std::cout << v3(0) << std::endl; 
    std::cout << v3(1) << std::endl; 
    std::cout << v3(2) << std::endl; 

    return 0; 
} 

Il codice sopra compilato ed eseguito in modo corretto, se mi Rimuovere il commento la dichiarazione di Vector<double,4> v4 ottengo il seguente errore in compilazione:

vec.cpp: In instantiation of ‘class Vector<double, 4u>’: 
vec.cpp:46:22: required from here 
vec.cpp:29:59: error: no type named ‘type’ in ‘struct std::enable_if<false, double>’ 

è qualcuno in grado di indicare dove sto andando male?

+0

possibile duplicato del [Posso usare boost :: abilitare \ _Se su una funzione di membro?] (Http: // StackOverflow .com/questions/4880922/can-i-use-boostenable-if-on-a-member-function) –

+1

Probabilmente vuoi 'typename std :: enable_if > :: type' piuttosto che 'Vector :: type, L>'. –

+1

(Probabilmente vuoi che il tuo 'operatore'() 'restituisca' T const & 'Anche il codice corrente consente ai client di fare qualcosa come' Vector v; Vector const & const_v = v; const_v (2) = 42.0; // Nota che la scrittura di 42 avviene temporaneamente. Non è quello che l'utente si aspettava!) –

risposta

8
template<unsigned LL = L> 
    Vector<typename std::enable_if<LL==3 && L == 3, T>::type, LL> 
    cross(const Vector<T,LL>& vec2) const 
    { 
    Vector<T,L> result; 

    result(0) = (*this)(1) * vec2(2) - (*this)(2) * vec2(1); 
    result(1) = (*this)(2) * vec2(0) - (*this)(0) * vec2(2); 
    result(2) = (*this)(0) * vec2(1) - (*this)(1) * vec2(0); 

    return result; 
    } 

PS. Perché funziona così?

La definizione della variabile v4 provoca un'istanziazione implicito del modello di classe Vector, che provoca, a sua volta, esemplificazione implicita delle dichiarazioni di funzioni membro classe, tra le altre cose (14.7.1 implicito istanziazione [temp.inst] # 1). Quest'ultima istanziazione, ovviamente, genera un errore.

Se invece cambiamo la funzione di membro per essere un modello di membro, secondo la stessa clausola, a questo punto il modello membro stesso viene istanziato e questo esemplificazione sembra, più o meno, come:

template<unsigned LL = 3> 
Vector<typename std::enable_if<LL==3 && 3 == 3, double>::type, LL> 
cross(const Vector<double,LL>& vec2) const; 

che è una dichiarazione modello completamente valida. Non facciamo (e non possiamo) eseguire ulteriori istanziazioni a questo punto.

Tuttavia, quando si tenta di chiamare effettivamente cross, questo è senza dubbio "un contesto che richiede l'esistenza della definizione di membro/funzione", in base a (14.7.1 Istanziazione implicita [temp.inst] # 2 , # 3), il modello cross (il secondo modello cross, quello che è il risultato dell'istanziazione del modello di classe esterna) viene implicitamente istanziato e allo std::enable_if viene data l'opportunità di fare il suo lavoro. Come nota a margine, questa è la situazione in cui è applicabile il principio SFINAE.

PPS. Per approfondire ulteriormente, anche se non direttamente connesso con la domanda OP, ma vale comunque la pena ricordare che non è sempre necessario dichiarare i membri come modelli per gestire situazioni simili.

ci sono situazioni in cui un membro di una classe template non è "valido" per una data di istanza, ma ancora il modello di classe è possibile creare un'istanza, ad esempio:

#include <type_traits> 

template<typename T> 
struct S 
{ 
    T x; 

    T foo() const { return x; } 

    typename std::remove_pointer<T>::type bar() const { return *x; } 
}; 

S<int> x; 
S<int *> y; 

A quanto pare, nella esemplificazione S<int> , l'espressione *x non è valida, poiché il tipo di x è int. Questo programma è corretto, però. Il punto importante è che durante l'istanza implicita solo le dichiarazioni dei membri vengono istanziate. Nel caso precedente, l'istanziazione S<int> causa l'istanziazione della dichiarazione int bar() const;, che è una dichiarazione completamente corretta.

Naturalmente, se poi cerchiamo di istanziare la definizione di S<int>::bar, come in:

void f() 
{ 
    x.foo(); 
    // x.bar(); // error 
    y.foo(); 
    y.bar(); 
} 

otterremo un errore.

(Questo segue ancora dalle sopra citate due paragrafi dello standard C++)

+0

Grazie, anche se non capisco perché questo è diverso da quello che avevo. È solo perché anche la funzione dei singoli membri deve essere basata sui modelli? – rozzy

+0

@ aero117, non viene generato alcun codice per una funzione basata su modello finché non viene creata un'istanza. Nel tuo codice, il compilatore genererà il codice per l'intera classe (quando il modello viene istanziato), inclusa la funzione. Ma quando si crea una funzione come modello, il codice per questa funzione verrà generato solo quando questo modello viene istanziato, ad esempio chiamando la funzione. – soon

+0

Complimenti per una spiegazione completa! –