check if member exists using enable_if

前端 未结 6 1275
鱼传尺愫
鱼传尺愫 2020-11-28 13:40

Here\'s what I\'m trying to do:

template  struct Model
{
    vector vertices ;

    #if T has a .normal member
    void transform(         


        
相关标签:
6条回答
  • 2020-11-28 13:44

    You need a meta function to detect your member so that you can use enable_if. The idiom to do this is called Member Detector. It's a bit tricky, but it can be done!

    0 讨论(0)
  • 2020-11-28 13:45

    I know this question already has some answers but I think my solution to this problem is a bit different and could help someone.

    The following example checks whether passed type contains c_str() function member:

    template <typename, typename = void>
    struct has_c_str : false_type {};
    
    template <typename T>
    struct has_c_str<T, void_t<decltype(&T::c_str)>> : std::is_same<char const*, decltype(declval<T>().c_str())>
    {};
    
    template <typename StringType,
              typename std::enable_if<has_c_str<StringType>::value, StringType>::type* = nullptr>
    bool setByString(StringType const& value) {
        // use value.c_str()
    }
    

    In case there is a need to perform checks whether passed type contains specific data member, following can be used:

    template <typename, typename = void>
    struct has_field : std::false_type {};
    
    template <typename T>
    struct has_field<T, std::void_t<decltype(T::field)>> : std::is_convertible<decltype(T::field), long>
    {};
    
    template <typename T,
              typename std::enable_if<has_field<T>::value, T>::type* = nullptr>
    void fun(T const& value) {
        // use value.field ...
    }
    

    UPDATE C++20

    C++20 introduced constraints and concepts, core language features in this C++ version.

    If we want to check whether template parameter contains c_str member function, then, the following will do the work:

    template<typename T>
    concept HasCStr = requires(T t) { t.c_str(); };
    
    template <HasCStr StringType> 
    void setByString(StringType const& value) {
        // use value.c_str()
    }
    

    Furthermore, if we want to check if the data member, which is convertible to long, exists, following can be used:

    template<typename T>
    concept HasField = requires(T t) {
        { t.field } -> std::convertible_to<long>;
    };
    
    template <HasField T> 
    void fun(T const& value) {
        // use value.field
    }
    

    By using C++20, we get much shorter and much more readable code that clearly expresses it's functionality.

    0 讨论(0)
  • 2020-11-28 13:45

    This isn't an answer to your exact case, but it is an alternative answer to the question title and problem in general.

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

    Relevant https://ideone.com/utZqjk.

    Credits to dyreshark on the freenode IRC #c++.

    0 讨论(0)
  • 2020-11-28 13:47
    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;
    }
    

    Using enable_if and decltype to let compiler to check variable, hope to help.

    0 讨论(0)
  • 2020-11-28 13:57

    This has become way easier with 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
        }
    };
    

    Things to note:

    • You can name non-static data members in decltype and sizeof without needing an object.
    • You can apply extended SFINAE. Basically any expression can be checked and if it is not valid when the arguments are substituted, the template is ignored.
    0 讨论(0)
  • 2020-11-28 13:58

    I know that it's little late, however...

    typedef int Matrix;
    
    struct NormalVertex {
        int pos;
        int normal;
    };
    
    struct Vertex {
        int pos;
    };
    
    template <typename T> struct Model
    {
        typedef int No;
        typedef char Yes;
    
        template<typename U> static decltype (declval<U>().normal, Yes()) has_normal(U a);
        static No has_normal(...);
    
        vector<T> vertices ;
    
        template <typename U = T>
        typename enable_if<sizeof(has_normal(declval<U>())) == sizeof(Yes), void>::type
        transform( Matrix m )
        {
            std::cout << "has .normal" << std::endl;
            for (auto vertex : vertices)
            {
              vertex.pos = m * vertex.pos ;
              vertex.normal = m * vertex.normal ;
            }
        }
    
        template <typename U = T>
        typename enable_if<sizeof(has_normal(declval<U>())) == sizeof(No), void>::type
        transform( Matrix m )
        {
            std::cout << "has no .normal" << std::endl;
            for (auto vertex : vertices)
            {
              vertex.pos = m * vertex.pos ;
            }
        }
    } ;
    
    int main()
    {
        Matrix matrix;
        Model <NormalVertex> normal_model;
    
        Vertex simple_vertex;
        Model <Vertex> simple_model;
    
        simple_model.transform(matrix);
        normal_model.transform(matrix);
    
        return 0;
    }
    
    0 讨论(0)
提交回复
热议问题