如果 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);
你可以强加 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
要在 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
并且可能需要构造函数。但至少它不会立即坏掉。
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);
你可以强加 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
要在 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
并且可能需要构造函数。但至少它不会立即坏掉。