编辑:我在使用SFINAE时犯了一个简单的错误。可以解决我在下面提到的编译器错误的修复程序。但是我仍然很好奇为什么在这种情况下不能推导出template参数。
我想编写一个C++ 14模板元程序来计算std::integer_sequence
的最大公约数(GCD)。经过一番修补后,我想到了这个几乎完整的示例:
template <typename T, T A, T B, T... Ints>
struct GCD<std::integer_sequence<T, A, B, Ints...>> :
GCD<typename std::integer_sequence<T, GCD_pair<T, A, B>::value, Ints...>> {};
template <class T, T A, T B>
struct GCD<std::integer_sequence<T, A, B>> :
GCD_pair<T, A, B> {};
int main() {
using seq = std::integer_sequence<int, 65537, 5, 10>;
cout << GCD<seq>::value << endl;
return 0;
}
我只是剥离整数序列的前两个元素,并使用待编写的
GCD_pair
元函数找到它们的GCD。然后,将GCD
应用于GCD_pair
和其余元素的结果。GCD_pair
的“显而易见”实现无法编译:// This does not work:
// type 'T' of template argument '0' depends on a template parameter
template <typename T, T M, T N>
struct GCD_pair : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {};
template <typename T, T M>
struct GCD_pair<T, M, 0> : std::integral_constant<T, M> {};
因此,我尝试了使用SFINAE的另一种可能的实现:
// This doesn't work either:
// template parameters not deducible in partial specialization
template <typename T, T M, T N, typename = void>
struct GCD_pair : std::integral_constant<T, M> {};
template <typename T, T M, T N, typename std::enable_if<(M % N != 0)>::type>
struct GCD_pair<T, M, N, void> : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {};
编辑:我犯了一个错误。请参阅以下答案,更正我对SFINAE的使用。但是我仍然对我的问题感到好奇:
为什么无法推导模板参数
typename std::enable_if<(M % N != 0)>::type
? 原则上是否不可推论,还是实际上可以推论出这种情况下的参数?换句话说,这是否可以视为编译器实现监督?对于它的值(value),我实际上可以通过将条件(
M % N != 0
)“隐藏”在bool
模板参数中来实现a slightly different version。但是,我认为以上两种都是合理的实现,因为operator%
以及与0
的operator!=
的比较对于所有C++的整数类型都是定义明确的。 最佳答案
它应该是:
template <typename T, T M, T N>
struct GCD_pair<T, M, N, typename std::enable_if<(M % N != 0)>::type>
: std::integral_constant<T, GCD_pair<T, N, M % N>::value> {};
因为您使用的是C++ 14,所以也可以使用
std::enable_if_t
简化它:template <typename T, T M, T N>
struct GCD_pair<T, M, N, std::enable_if_t<(M % N != 0)>>
: std::integral_constant<T, GCD_pair<T, N, M % N>::value> {};
在这两种情况下,如果条件
(M % N != 0)
都适用,则一旦实例化主模板和特化都有效,但是特化会更加特化,因此可以选择它。另一方面,如果条件不适用,则由于有限制规则,特化将被静默丢弃,但是主模板仍然有效,因此被选择。
还请注意,当条件为true时,
type
中的typename std::enable_if<(M % N != 0)>::type
是void
。因此,您的模板参数列表理论上将是:
template <typename T, T M, T N, void>
struct GCD_pair<T, M, N, void>: std::integral_constant<T, GCD_pair<T, N, M % N>::value> {};
但是,
void
不允许作为非类型模板参数。最后,从标准中我们可以得出:
还:
在您的情况下,由于明显的原因,无法推导出特化的第四个参数。即使它是有效的(并且不是有效的,因为它会导致如上所述的
void
),但您得到的是类型或非类型参数,而您没有该类型或值的实际类型或值。假设您具有以下专长:
template <typename T, T M, T N, int>
struct GCD_pair<T, M, N, void> : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {};
编译器如何推断最后一个模板参数的值?
不能,那或多或少取决于您的情况。
如果
std::enable_if
的条件有效,它应该宁可检测出参数列表格式错误的事实,无论如何都是错误,并且其中一个已退出编译阶段。您将从中受益的是这样的事情:
template <typename T, T M, T N, typename = std::enable_if_t<(M % N != 0)>>
struct GCD_pair<T, M, N, void> : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {};
或这个:
template <typename T, T M, T N, std::enable_if_t<(M % N != 0)>* = nullptr>
struct GCD_pair<T, M, N, void> : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {};
无论如何,它们都无效,因为在部分特化中不允许使用默认参数。
关于c++ - 为什么无法推导此模板参数?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/39784727/