C++ C++;为什么SFINAE只使用类模板参数失败?

C++ C++;为什么SFINAE只使用类模板参数失败?,c++,templates,c++11,sfinae,C++,Templates,C++11,Sfinae,我以的样式使用SFINAE,以便通过使用适当的成员函数调用通用向量对象。例如,以下代码首先调用运算符[](int)const,如果不存在,则调用运算符()(int)const: template<int I> struct rank : rank<I-1> { static_assert(I > 0, ""); }; template<> struct rank<0> {}; template<typename VectorType&

我以的样式使用SFINAE,以便通过使用适当的成员函数调用通用向量对象。例如,以下代码首先调用
运算符[](int)const
,如果不存在,则调用
运算符()(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;
};
但是,现在编译失败(在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;
/usr/local/include/c++/5.1.0/type_traits:替换“模板使用enable_if_t=typename std::enable_if::type[带bool _Cond=has_括号_操作符::value;_Tp=void]:
main.cpp:46:10:必须来自“struct VectorWrapper”
main.cpp:59:38:从此处开始需要
/usr/local/include/c++/5.1.0/type_traits:2388:61:错误:“struct std::enable_if”中没有名为“type”的类型
使用enable_if_t=typename enable_if::type;

问题:

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

在失败的示例中,模板参数
VectorType
在解析get时已经确定。要使SFINAE工作,您需要在该方法调用时设置用于SFINAE解析的模板参数。以下是对第一个示例的修改,以使其按照您的意愿工作:

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=VectorType, typename = std::enable_if_t<has_bracket_operator<const V>::value> >
    auto get(int i, rank<2>) const
    {
        return v[i];
    }

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

    VectorType v;
};
template-struct-rank:rank{static_-assert(I>0,”);};
模板结构秩{};
模板
结构向量包装器
{
自动获取(int i)常量
{
return-get(v,i,rank());
}
模板
自动获取(整数i,秩)常量
{
返回v[i];
}
模板
自动获取(整数i,秩)常量
{
返回v(i);
}
向量v型;
};

这样,当调用
get
时,
V
被解析,它将正确使用SFINAE。

SFINAE来自[temp.decrete]/8,重点是:

如果替换导致无效的类型或表达式,则类型推断将失败。输入了无效的类型或表达式 如果使用替换参数编写,则格式错误,需要诊断。[注: 如果不需要诊断,则程序仍然格式不正确。访问检查作为替换的一部分进行 进程.-结束注释]仅在函数类型的直接上下文中使用无效类型和表达式 其模板参数类型可能导致扣减失败

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

template<typename V, typename = std::enable_if_t<has_bracket_operator<const V>::value> >
auto get(V const& v, 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
模板
自动获取(整数i,秩)常量
然后调用
get()

,或者您可以使用标记分派:

自动获取(int i)常量
{
返回get(i,has_括号_运算符(),has_括号_运算符());
}
自动获取(int i,std::true\u type/*括号*/,std::false\u type/*括号*/)常量
{
返回v[i];
}
自动获取(int i,std::false_type/*方括号*/,std::true_type/*方括号*/)常量
{
返回v(i);
}

如果向量没有
操作符()(int)
,我想这会失败。你为什么这么认为。我不明白你的话。向量没有
operator()(int)
,因为我认为编译器会实例化这些函数,而不管传递给它的参数是什么。当它试图实例化
操作符()
版本时,它失败了。我猜表达式SFINAE正在工作:由于
decltype(v(I))
格式不正确,该函数可能会自动禁用(?)。@davidhigh类模板的成员函数仅在使用时实例化。我不确定“即时上下文”是否与此处相关(尽管解决方案是合理的)。这是。@T.C.这是否意味着不编译是正确的,但可能不应该,或者它应该编译,而编译器出错了?我不知道CWG将如何处理这个问题。现在我可能会称之为未指定。
template<typename = std::enable_if_t<has_bracket_operator<VectorType>::value> >
auto get(int i, rank<2>) const
template<typename T=VectorType, typename = std::enable_if_t<has_bracket_operator<T>::value> >
auto get(int i, rank<2>) const
auto get(int i) const
{
    return get(i, has_bracket_operator<VectorType>(), has_parenthesis_operator<VectorType>());
}

auto get(int i, std::true_type /*brackets*/, std::false_type /*parenthesis*/) const
{
    return v[i];
}

auto get(int i, std::false_type /*brackets*/, std::true_type /*parenthesis*/) const
{
    return v(i);
}