使用enable_if检查成员是否存在

时间:2012-12-09 11:12:48

标签: c++ templates sfinae

这是我正在尝试做的事情:

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

我看过examples使用enable_if,但我无法理解如何将enable_if应用于此问题,或者是否可以应用此问题。

6 个答案:

答案 0 :(得分:26)

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

注意事项:

  • 您可以在decltypesizeof中为非静态数据成员命名,而无需使用对象。
  • 您可以申请延期SFINAE。基本上可以检查任何表达式,如果在替换参数时它无效,则忽略模板。

答案 1 :(得分:7)

您需要使用元函数来检测您的成员,以便您可以使用enable_if。这样做的成语称为Member Detector。这有点棘手,但可以做到!

答案 2 :(得分:2)

这不是您确切案例的答案,但它通常是问题标题和问题的替代答案。

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

相关https://ideone.com/utZqjk

在freenode IRC上对dyreshark的信用#c ++

答案 3 :(得分:1)

我知道还来不及...

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

答案 4 :(得分:1)

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

使用enable_if和decltype让编译器检查变量,希望对您有所帮助。

答案 5 :(得分:0)

我知道这个问题已经有了一些答案,但是我认为我对这个问题的解决方案有些不同,可以帮助某人。

以下示例检查传递的类型是否包含c_str()函数成员:

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()
}

如果需要检查传递的类型是否包含特定的数据成员,则可以使用以下内容:

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在此C ++版本中引入了约束和概念,核心语言功能。

如果我们要检查模板参数是否包含c_str成员函数,则将执行以下操作:

template<typename T>
concept HasCStr = requires(T t) { t.c_str(); };

template <HasCStr StringType> 
void setByString(StringType const& value) {
    // use value.c_str()
}

此外,如果我们要检查是否存在可转换为long的数据成员,则可以使用以下内容:

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
}

通过使用C ++ 20,我们获得了更短,更易读的代码,清楚地表达了其功能。