2012-12-09 20 views
20

Ecco quello che sto cercando di fare:controllo se membro esista utilizzando enable_if

template <typename T> struct Model 
{ 
    vector<T> vertices ; 

    #if T has a .normal member 
    void transform(Matrix m) 
    { 
     each vertex in vertices 
     { 
      vertex.pos = m * vertex.pos ; 
      vertex.normal = m * vertex.normal ; 
     } 
    } 
    #endif 

    #if T has NO .normal member 
    void transform(Matrix m) 
    { 
     each vertex in vertices 
     { 
      vertex.pos = m * vertex.pos ; 
     } 
    } 
    #endif 
} ; 

Ho visto examples di usare enable_if, ma non riesco a capire come applicare enable_if a questo problema, o se addirittura può essere applicato.

+1

'enable_if' non viene utilizzato per verificare se esiste un membro, ma viene utilizzato per rimuovere i sovraccarichi. – Pubby

+0

Non posso usarlo per fare qualcosa di simile (suggerimento in modifica sopra)? – bobobobo

+0

No, stai cercando un 'statico if' che non esiste ancora. Quello che vuoi è completamente possibile, semplicemente non userà la sintassi come quella. – Pubby

risposta

23

Questo è diventato modo più semplice con C++ 11.

template <typename T> struct Model 
{ 
    vector<T> vertices; 

    void transform(Matrix m) 
    { 
     for(auto &&vertex : vertices) 
     { 
      vertex.pos = m * vertex.pos; 
      modifyNormal(vertex, m, special_()); 
     } 
    } 

private: 

    struct general_ {}; 
    struct special_ : general_ {}; 
    template<typename> struct int_ { typedef int type; }; 

    template<typename Lhs, typename Rhs, 
      typename int_<decltype(Lhs::normal)>::type = 0> 
    void modifyNormal(Lhs &&lhs, Rhs &&rhs, special_) { 
     lhs.normal = rhs * lhs.normal; 
    } 

    template<typename Lhs, typename Rhs> 
    void modifyNormal(Lhs &&lhs, Rhs &&rhs, general_) { 
     // do nothing 
    } 
}; 

cose da notare:

  • è possibile denominare membri di dati non statici in decltype e sizeof senza bisogno di un oggetto.
  • È possibile applicare SFINAE estesa. Fondamentalmente qualsiasi espressione può essere controllata e se non è valida quando gli argomenti sono sostituiti, il modello viene ignorato.
+0

Questo è sicuramente molto meglio SFINAE di [prima] (http://stackoverflow.com/a/6324863/111307), in cui è necessario _creare_ un rilevatore di membri per ciascun tipo di membro che si desidera rilevare. Ma perché le persone saltano sempre su SFINAE, preferisco usare [caratteri tipografici] (http://stackoverflow.com/questions/13787490/how-do-you-use-type-traits-to-do-conditional-compilation) per fare questo tipo di compilazione condizionale ora. – bobobobo

+0

Immagino che dipenda se hai più tipi da controllare o più membri da controllare? –

+1

I caratteri del tipo sono utili solo se si conoscono i tipi in anticipo. Nel tuo link, devi sapere che VertexN è la classe speciale. Quindi potrebbero funzionare nell'esempio di OP, ma SFINAE funziona anche se non sai quali saranno gli altri tipi, quale IMO è l'incapsulamento molto migliore. –

6

È necessaria una funzione meta per rilevare il membro in modo che sia possibile utilizzare enable_if. L'idioma per farlo è chiamato Member Detector. È un po 'complicato, ma può essere fatto!

1

Questa non è una risposta al tuo caso esatto, ma è una risposta alternativa al titolo della domanda e al problema in generale.

#include <iostream> 
#include <vector> 

struct Foo { 
    size_t length() { return 5; } 
}; 

struct Bar { 
    void length(); 
}; 

template <typename R, bool result = std::is_same<decltype(((R*)nullptr)->length()), size_t>::value> 
constexpr bool hasLengthHelper(int) { 
    return result; 
} 

template <typename R> 
constexpr bool hasLengthHelper(...) { return false; } 

template <typename R> 
constexpr bool hasLength() { 
    return hasLengthHelper<R>(0); 
} 

// function is only valid if `.length()` is present, with return type `size_t` 
template <typename R> 
typename std::enable_if<hasLength<R>(), size_t>::type lengthOf (R r) { 
    return r.length(); 
} 

int main() { 
    std::cout << 
     hasLength<Foo>() << "; " << 
     hasLength<std::vector<int>>() << "; " << 
     hasLength<Bar>() << ";" << 
     lengthOf(Foo()) << 
     std::endl; 
    // 1; 0; 0; 5 

    return 0; 
} 

Rilevante https://ideone.com/utZqjk.

Crediti per dyreshark su freenode IRC# C++.

Problemi correlati