使用enable_if检查成员是否存在

bob*_*obo 29 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
} ;
Run Code Online (Sandbox Code Playgroud)

我已经看过使用的例子enable_if,但是我无法理解如何应用enable_if这个问题,或者甚至可以应用它.

Joh*_*itb 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
    }
};
Run Code Online (Sandbox Code Playgroud)

注意事项:

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

  • 类型特征仅在您提前知道类型的情况下才有用。在您的链接中,您必须知道VertexN是特殊类。因此它们可以在OP的示例中工作,但是SFINAE可以工作,即使您不知道其他类型将是什么,IMO封装也更好。 (2认同)

ltj*_*jax 7

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


Nut*_*ker 7

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

以下示例检查传递的类型是否包含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()
}
Run Code Online (Sandbox Code Playgroud)

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

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 ...
}
Run Code Online (Sandbox Code Playgroud)

更新 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()
}
Run Code Online (Sandbox Code Playgroud)

此外,如果我们想检查可转换为 的数据成员是否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
}
Run Code Online (Sandbox Code Playgroud)

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