web-dev-qa-db-fra.com

vérifier si le membre existe en utilisant enable_if

Voici ce que j'essaie de faire:

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
} ;

J'ai vu exemples d'utiliser enable_if, mais je ne comprends pas comment postuler enable_if à ce problème, ou s'il peut même être appliqué.

30
bobobobo

Cela est devenu façon plus facile avec 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
    }
};

À noter:

  • Vous pouvez nommer des membres de données non statiques dans decltype et sizeof sans avoir besoin d'un objet.
  • Vous pouvez appliquer SFINAE étendu. Fondamentalement, toute expression peut être vérifiée et si elle n'est pas valide lorsque les arguments sont substitués, le modèle est ignoré.
26

Vous avez besoin d'une méta-fonction pour détecter votre membre afin de pouvoir utiliser enable_if. L'idiome pour ce faire est appelé Détecteur de membres . C'est un peu délicat, mais cela peut être fait!

7
ltjax

Ce n'est pas une réponse à votre cas exact, mais c'est une réponse alternative au titre de la question et au problème en général.

#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;
}

Pertinent https://ideone.com/utZqjk .

Crédits pour la coloration des images sur le freenode IRC #c ++ .

2
deceleratedcaviar
template<
typename HTYPE, 
typename = std::enable_if_t<std::is_same<decltype(HTYPE::var1), decltype(HTYPE::var1)>::value>
>
static void close_release
(HTYPE* ptr) {
    ptr->var1;
}

Utilisation de enable_if et decltype pour permettre au compilateur de vérifier la variable, espérons vous aider.

0
vrqq