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不是可返回类型的类型,是否可以排除成员模板?
答案 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);
您可以强加U
和T
是同一类型
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);