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

disable template member function if return type is an array

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 会 return 一个数组。

(我假设成员模板没有被实例化,因为它没有被使用——显然,这种简单化的观点是错误的。)

为什么SFINAE这里不适用?

有没有办法排除 T 不是 returnable 类型的类型的成员模板?

SFINAE 从重载集中删除在模板参数推导期间非法的重载。

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

然后,类型Container<const char (&)[7]>被实例化。并且它生成一个模板函数 (Container<const char (&)[7]>::find_if),其签名是非法的(所有这些,因为 T 是在 find_if 的上下文中推导出来的)。 SFINAE 不在游戏中。

现在,您可以将一些 SFINAE 添加到容器的 find_if 函数中,方法是使其 return 类型取决于其模板参数。为此,请参阅

试试

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

SFINAE 在方法上不适用于 class 的模板参数。在方法本身的模板上工作。因此,您必须使 SFINAE 替换依赖于方法本身的模板参数。

所以不是 T,而是 U

如果您担心有人可以 "hijack" 您的函数,请按如下说明模板类型

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

要在 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);

SFINAE 没有发挥作用,因为在 MakeContainer 重载的 SFINAE 期间未检查在您的 MakeContainer return 点中生成的类型的成员。

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 并且可能需要构造函数。但至少它不会立即坏掉。