Joh*_*uck 5 c++ templates compiler-errors template-meta-programming c++14
编辑:我在使用SFINAE时犯了一个简单的错误.修复解决了我在下面提到的编译器错误.但是我仍然很好奇为什么在这种情况下不能推导出模板参数.
我想写一个C++ 14模板元程序来计算a的最大公约数(GCD)std::integer_sequence
.经过一些修修补补后,我想出了这个几乎完整的例子:
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;
}
Run Code Online (Sandbox Code Playgroud)
我只是剥离整数序列的前两个元素,并使用待写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> {};
Run Code Online (Sandbox Code Playgroud)
所以我尝试了使用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> {};
Run Code Online (Sandbox Code Playgroud)
编辑:我犯了一个错误.请参阅下面的答案,更正我对SFINAE的使用.但我仍然对我的问题感到好奇:
为什么模板参数typename std::enable_if<(M % N != 0)>::type
不可推导?这难道不是推断出以往的原则,或可能的参数,如在这种情况下,实际上在实践中得出?换句话说,这可能被认为是编译器实现的疏忽吗?
为了它的价值,我能够通过在模板参数中"隐藏"conditional()来实现稍微不同的版本.然而,我认为上述两种都是合理的实现,因为与比较与对于所有C++的积分类型是完全明确定义的.M % N != 0
bool
operator%
0
operator!=
它应该是:
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> {};
Run Code Online (Sandbox Code Playgroud)
因为您使用的是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> {};
Run Code Online (Sandbox Code Playgroud)
在这两种情况下,如果条件(M % N != 0)
适用,主模板和专业化在实例化后都是有效的,但专业化更加专业化,因此选择它.
另一方面,如果条件不适用,则由于sfinae规则而静默地丢弃特化,但主模板仍然有效并因此被选择.
还需要注意的是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> {};
Run Code Online (Sandbox Code Playgroud)
但是,void
不允许将其作为非类型模板参数.
最后,从标准我们得到:
如果可以从实际模板参数列表推导出部分特化的模板参数,则部分特化匹配给定的实际模板参数列表
也:
如果由于其template-parameter-list和template-id的结构而无法推断出部分特化的模板参数,则该程序是不正确的.
在您的情况下,由于显而易见的原因,无法推断出专业化的第四个参数.即使它是有效的(并且它不是,因为它导致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> {};
Run Code Online (Sandbox Code Playgroud)
编译器如何推导出最后一个模板参数的值?
它不能,而且或多或少都是你的情况.可能它应该检测到参数列表是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> {};
Run Code Online (Sandbox Code Playgroud)
或这个:
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> {};
Run Code Online (Sandbox Code Playgroud)
无论如何,在部分特化中不允许它们都对默认参数无效.