考虑到以下例子:
#include<iostream>
template<int n>
struct fibonacci {
static const int value = n < 0 ? 0 : fibonacci<n-1>::value + fibonacci<n-2>::value;
};
template<>
struct fibonacci<1> {
static const int value = 1;
};
template<>
struct fibonacci<0> {
static const int value = 0;
};
int main() {
std::cout << fibonacci<-1>::value << std::endl;
return 0;
}
我熟悉C++中的惰性计算,并且期望在传递参数<0时,不会计算if语句的第二个分支泛型fibonacci模板。但是,编译代码仍然会导致来自该分支的无限循环:
Fibonacci.cpp: In instantiation of ‘const int fibonacci<-900>::value’:
Fibonacci.cpp:5:58: recursively required from ‘const int fibonacci<-2>::value’
Fibonacci.cpp:5:58: required from ‘const int fibonacci<-1>::value’
Fibonacci.cpp:20:33: required from here
Fibonacci.cpp:5:58: fatal error: template instantiation depth exceeds maximum of 900 (use ‘-ftemplate-depth=’ to increase the maximum)
5 | static const int value = n < 0 ? 0 : fibonacci<n-1>::value + fibonacci<n-2>::value;
| ^~~~~
为什么是这种情况?它是否特定于与模板结构相关的东西?
发布于 2020-12-14 16:51:19
我熟悉C++中的惰性计算,并且期望在传递参数<0时,不会计算if语句的第二个分支泛型fibonacci模板。
它不需要被评估。但我们不是在处理评估问题。我们正在处理模板实例化。您使用了fibonacci<n-1>::value
,这需要实例化完整的对象类型fibonacci<n-1>
。必须检查该类型,以确定它是否有一个成员value
,可以在这样的表达式中使用。
实例化类模板会导致其成员的声明被实例化。静态数据成员的声明包含一个初始化器,因此也必须实例化它。因此,我们需要递归地实例化模板。
简单地命名fibonacci<n-1>
并不会导致它被实例化(想一想前面的声明)。如果要延迟实例化,则必须以需要定义这些类型(例如访问成员)的方式延迟使用这些类型。
这方面的旧的元编程技巧(与函数式编程非常一致)涉及到助手模板。
template<class L, class R>
struct add {
static constexpr auto value = L::value + R::value;
};
template<int n>
struct fibonacci {
static const int value = std::conditional_t<(n < 0), fibonacci<0>, add<fibonacci<n-1>, fibonacci<n-2>>>::value;
};
std::conditional_t
将根据条件选择一个类型。然后,访问该类型的::value
(并且只访问该类型)。因此,在实际需要之前,没有什么是完全实例化的。
发布于 2020-12-14 16:47:15
您可以使用if constexpr
template<int n>
struct fibonacci {
static const int value = []() {
if constexpr (n < 0) {
return 0;
} else {
return fibonacci<n-1>::value + fibonacci<n-2>::value;
}
}();
};
发布于 2020-12-14 16:52:31
当用fibonacci
的某个值实例化n
时,还必须编译此实例化中使用的所有表达式。这意味着所使用的任何模板也必须实例化。这是必要的,即使包含模板实例化的表达式从未被计算过。
避免在表达式中实例化模板的唯一方法是根本不编译表达式。这使您可以避免使用不正确的参数实例化模板。
您可以通过使用来自if constexpr
的C++17来实现这一点:
template<int n>
struct fibonacci {
static const int value = [] {
if constexpr (n < 0) return 0;
else return fibonacci<n-1>::value + fibonacci<n-2>::value;
}();
};
这是一个演示。
https://stackoverflow.com/questions/65292785
复制相似问题