如果返回类型是数组,则禁用模板成员函数

时间:2018-11-26 16:35:25

标签: c++ templates sfinae template-deduction

https://www.godbolt.org/z/_4aqsF

template <typename T> struct Container
{
    template <typename TPred> T find_if(TPred pred);  // the culprit
};

template <typename T> Container<T> MakeContainer(T const &)
{
    return Container<T>();    
}

int main()
{
    auto x = MakeContainer("Hello!");
}

gcc,clang和msvc显然同意不能编译,因为find_if将返回数组。

(我本以为成员模板不会被实例化,因为它没有被使用-显然,这种简单的视图是错误的。)

为什么SFINAE在这里不适用?

对于T不是可返回类型的类型,是否可以排除成员模板?

4 个答案:

答案 0 :(得分:4)

SFINAE不起作用,因为在MakeContainer重载的SFINAE期间,不会检查在您的MakeContainer返回点中生成的类型的成员。

SFINAE仅在近期内发生。类型和功能的实体不在范围内,不会引起替换失败。

template <typename T=char[7]> Container<char[7]> MakeContainer(char const (&)[7])

这个签名很好。

选择后,将实例化Container<char[7]>并解析其方法。

template <typename TPred> char[7] find_if(TPred pred);  // the culprit

没有TPred可能导致此find_if成为有效方法,因此您的程序没有诊断依据。

正确的解决方法是:

template <typename T> struct Container
{
  template <typename TPred> T find_if(TPred pred);  // the culprit
};
template <class T, std::size_t N> struct Container<T[N]>:
  Container<std::array<T,N>>
{
  using Container<std::array<T,N>>::Container;
};

当然,Container<std::array<T,N>>本身需要一个非常特殊的find_if甚至可能是构造函数。但是至少它不会立即中断。

答案 1 :(得分:3)

SFINAE从重载集中删除在模板参数推导过程中是非法的重载。

此处,重载集仅包含一个候选项:MakeContainer<const char (&)[7]>。模板参数推论到此结束。没有歧义。一切都很好。

然后,实例化类型Container<const char (&)[7]>。并生成其签名无效的模板化函数(Container<const char (&)[7]>::find_if)(由于T是在find_if的上下文中推导出来的,因此它们的签名是非法的)。 SFINAE不在比赛中。

现在,您可以通过使其容器的返回类型取决于其模板参数,来向容器的find_if函数中添加一些SFINAE。为此,请参见max66's answer

答案 2 :(得分:2)

尝试

template <typename TPred, typename U = T>
U find_if (TPred pred);  // the culprit

SFINAE(通过方法)不适用于该类的模板参数。处理方法本身的模板。因此,必须使SFINAE替换依赖于方法本身的模板参数。

不是T,而是U

如果您担心有人可以“劫持”您的函数,从而按如下方式说明模板类型

auto x = MakeContainer("Hello!");

x.find_if<int, int>(1);

您可以强加UT是同一类型

template <typename TPred, typename U = T>
typename std::enable_if<std::is_same<U, T>::value, U>::type
    find_if (TPred pred)  // the culprit

答案 3 :(得分:2)

要在fidn_if上使用SFINAE,您需要使用函数本身的相关参数,这是SFINAE在不可返回类型上的版本:

template <typename TPred, class U = T, typename std::enable_if<
       std::is_same<T, U>::value
    && !std::is_abstract<U>::value
    && !std::is_function<U>::value
    && !std::is_array<U>::value
    , bool>::type = true>
U find_if(TPred pred);