c++ - 基于类成员存在/不存在的 SFINAE

标签 c++ c++11 sfinae

我对 SFINAE 有基本的了解,例如如何enable_if作品。我最近遇到了this answer ,我花了一个多小时试图了解它的实际工作原理,但无济于事。

这段代码的目标是根据类中是否有特定成员来重载函数。这是复制的代码,它使用 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
    }
};

对于我的生活,我无法理解这种机制是如何工作的。具体来说,typename int_<decltype(Lhs::normal)>::type = 0 是什么意思?帮助我们做到这一点,为什么我们需要在此方法中使用额外的类型 ( special_/general_ )。

最佳答案

why do we need an extra type (special_/general_) in this method

这些仅用于允许 modifyNormal 的目的函数被不同的实现重载。他们的特别special_使用 IS-A 关系,因为它继承自 general_ .此外,transform函数总是调用 modifyNormal重载需要 special_键入,请参阅下一部分。

what does typename int_<decltype(Lhs::normal)>::type = 0 help us do

这是一个带有默认值的模板参数。默认值存在,因此 transform函数不必指定它,这很重要,因为另一个 modifyNormal函数没有此模板参数。此外,添加此模板参数仅用于调用 SFINAE。

http://en.cppreference.com/w/cpp/language/sfinae

When substituting the deduced type for the template parameter fails, the specialization is discarded from the overload set instead of causing a compile error.

因此,如果发生故障,modifyNormal函数取 special_ type 从要考虑的重载集中删除。这只剩下 modifyNormal函数取 general_输入并自 special_ IS-A general_输入一切仍然有效。

如果没有发生替换失败,则 modifyNormal使用 special_ 的函数type 将被使用,因为它是更好的匹配。


注意: general_类型是 struct所以继承是public默认情况下,允许 IS-A 关系而不使用 public关键字。


编辑:

Can you comment on why we use the elaborate typename int_<decltype(Lhs::normal)>::type mechanism in the first place?

如上所述,这用于触发 SFINAE 行为。但是,当您将其分解时,它并不是很详尽。它的核心是要实例化 int_ 的一个实例。某种类型的结构 T它有一个 type定义的数据类型:

int_<T>::type

由于这是在模板中使用的 typename需要添加关键词,见When is the “typename” keyword necessary? .

typename int_<T>::type

最后,用于实例化 int_ 的实际类型是什么?结构?这是由 decltype(Lhs::normal) 决定的,报告 Lhs::normal 的类型.如果输入 Lhs类型有一个 normal数据成员然后一切成功。但是,如果不是,则表示替换失败,这一点的重要性已在上面解释。

关于c++ - 基于类成员存在/不存在的 SFINAE,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36194449/

相关文章:

c++ - 基类 C++14 模板函数在 Mac OS 上的 clang 中不可见(递归模板)

c++ - 用户定义的转换为模板类引用的编译器行为不一致

c++ - 尝试包装函数返回值时出现 "<class name> does not provide a call operator"错误

C++11 for 模板函数中的循环

c++ - 视觉 C++ 2008 : Finding the cause of slow link times

c++ - 如何在 C++ 中创建自定义整数序列

c++ - 使用已定义结构的 vector 达到未初始化的内存

c++ - 以不同方式分派(dispatch)右值和左值并使用 sfinae 禁用一个选项

c++ - 对象没有命名类型 - C++

C++17 表达式求值顺序和 std::move