check if member exists using enable_if

Solution 1:

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.

Solution 2:

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.

Solution 3:

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!

Solution 4:

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++.