C++ 为什么 SFINAE 仅因类模板参数而失败?

标签 c++ templates c++11 sfinae

我正在以 this answer 的风格使用 SFINAE为了通过使用适当的成员函数调用通用 vector 对象。例如,以下代码首先调用 operator[](int) const,如果不存在则调用 operator()(int) const:

template<int I> struct rank : rank<I-1> { static_assert(I > 0, ""); };
template<> struct rank<0> {};

template<typename VectorType>
struct VectorWrapper
{
    auto get(int i) const
    {
        return get(v, i, rank<5>());
    }

    template<typename V, typename = std::enable_if_t<has_bracket_operator<const V>::value> >
    auto get(V const& v, int i, rank<2>) const
    {
        return v[i];
    }

    template<typename V, typename = std::enable_if_t<has_parenthesis_operator<const V>::value> >
    auto get(V const& v, int i, rank<1>) const
    {
        return v(i);
    }

    VectorType v;
};

使用 has_bracket_operatorhas_parenthesis_operator 特征按照 this thread 中的建议设置, 整体编译和seems to work .

然而,将成员 vector 传递给重载的类模板从一开始似乎是不必要的,所以我尝试设置相同但不传递它。为此,我将模板参数 V 替换为用于设置类模板的 VectorType 参数:

    template<typename = std::enable_if_t<has_bracket_operator<VectorType>::value> >
    auto get(int i, rank<2>) const
    {
        return v[i];
    }

    template<typename = std::enable_if_t<has_parenthesis_operator<VectorType>::value> >
    auto get(int i, rank<1>) const
    {
        return v(i);
    }

但是现在,编译失败(在 gcc 5.1.0 中)并出现以下错误消息:

/usr/local/include/c++/5.1.0/type_traits: In substitution of 'template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = has_parenthesis_operator<std::vector<int> >::value; _Tp = void]':
main.cpp:46:10:   required from 'struct VectorWrapper<std::vector<int> >'
main.cpp:59:38:   required from here
/usr/local/include/c++/5.1.0/type_traits:2388:61: error: no type named 'type' in 'struct std::enable_if<false, void>'
     using enable_if_t = typename enable_if<_Cond, _Tp>::type;

DEMO

问题:

  • 这个编译错误的原因是什么?
  • 除了我的第一个代码块之外,是否有合适的解决方法? (也就是说,一种保留通常编码风格的方式——其中一种方式不必传递成员)。

最佳答案

SFINAE 来自 [temp.deduct]/8,强调我的:

If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed, with a diagnostic required, if written using the substituted arguments. [ Note: If no diagnostic is required, the program is still ill-formed. Access checking is done as part of the substitution process. —end note ] Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure.

直接上下文是模板声明中的内容。在您的初始示例中:

template<typename V, typename = std::enable_if_t<has_bracket_operator<const V>::value> >
auto get(V const& v, int i, rank<2>) const

V在直接上下文中,因此 enable_if 上的替换失败只是推演失败。

但是,在您的第二个示例中:

template<typename = std::enable_if_t<has_bracket_operator<VectorType>::value> >
auto get(int i, rank<2>) const

VectorType不在 get 的直接上下文中,所以这里的失败不会是演绎失败,而是硬错误。

除非VectorType恰好有所有这些运算符。

任何模板问题的解决方案就是添加更多模板。在这种情况下,强制 VectorType通过引入另一种类型进入直接上下文:

template<typename T=VectorType, typename = std::enable_if_t<has_bracket_operator<T>::value> >
auto get(int i, rank<2>) const

然后调用get<>() .

关于C++ 为什么 SFINAE 仅因类模板参数而失败?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30855339/

相关文章:

c++ - 在常数时间内修剪 C++ 字符串

c++ - yaml-cpp 以什么顺序返回数据?

c++ - 什么时候评估 'auto' 和 'decltype'?

c++ - C、C++、python、perl在Web开发中的作用

c# - C 中的 SortedDictionary

c++ - 测试容器是否实现 .at() 成员访问/std::sort 兼容的正确方法

html - Pug 模板不会渲染传递给它的原始 HTML

c++ - 获取具有通用返回类型的函数

c++ - std::string — 小字符串优化和交换

c++ - 在 C++ 11 中使用来自 lambda 的值