C++ 可以在C+;中使用for循环实例化模板+;14 constexpr函数?
我一直在用一个SVN构建的clang来尝试C++ 可以在C+;中使用for循环实例化模板+;14 constexpr函数?,c++,c++14,constexpr,C++,C++14,Constexpr,我一直在用一个SVN构建的clang来尝试constexpr的宽松规则。到目前为止,我还无法确定的一件事是,是否可以在编译时在constexpr函数中循环遍历元组中的元素 因为我没有C++14兼容的标准库可供测试,所以我准备了以下等效测试: template<int N> constexpr int foo() { return N; } constexpr int getSum() { auto sum = 0; for (auto i = 0; i < 10;
constexpr
的宽松规则。到目前为止,我还无法确定的一件事是,是否可以在编译时在constexpr函数中循环遍历元组中的元素
因为我没有C++14兼容的标准库可供测试,所以我准备了以下等效测试:
template<int N>
constexpr int foo() {
return N;
}
constexpr int getSum() {
auto sum = 0;
for (auto i = 0; i < 10; ++i) {
sum += foo<i>();
}
return sum;
}
constexpr auto sum = getSum();
在SVN clang中,我的第一个示例没有:
test2.cpp:19:12: error: no matching function for call to 'foo'
sum += foo();
^~~~~~
test2.cpp:11:15: note: candidate template ignored: invalid explicitly-specified
argument for template parameter 'N'
constexpr int foo() {
^
test2.cpp:19:12:错误:调用“foo”时没有匹配的函数
sum+=foo();
^~~~~~
test2.cpp:11:15:注意:忽略候选模板:显式指定无效
模板参数“N”的参数
constexpr int foo(){
^
首先,我很难解释这个错误消息的第二部分。除此之外,它是由C++14标准强制执行的吗?如果是的话,有人知道为什么不允许使用这种语法(简单的疏忽或防止某些事情发生)
除此之外,它是由C++14标准强制执行的吗?如果是的话,有人知道为什么不允许使用这种语法(简单的监督或保护不受影响)
这是因为constexpr
并不是编译时计算或使用的专用函数。constexpr
函数就是这样,允许函数(或变量)在常量表达式中使用。除此之外,它们是正则函数。在某些上下文中,例如static\u assert
或数组大小等仅在编译时使用的情况下,恰好需要常量表达式
你会注意到在你的代码中你循环了一个变量,但是你循环的变量本身并不是constexpr
,所以它不是一个常量表达式,不能在N
的模板实例化中使用
constexpr bool f(int x) {
static_assert(x > 10, "..."); // invalid
return true;
}
这显然是无效的,因为正如我前面提到的,您不必在独占编译时情况下使用constexpr
函数
constexpr int times_ten(int x) {
return x * 10;
}
int main() {
int a = times_ten(20); // notice, not constexpr
static_assert(times_ten(20) == 200, "...");
static_assert(a == 200, "..."); // doesn't compile
}
这对你来说可能太晚了,但对其他人来说可能会有用,我的答案如下 @Rapptz的答案没有错,但是你的问题的答案可能是“是”。是的,for循环不能像我们讨论的那样使用。但是你想在循环中迭代,不必使用for循环思维,例如使用递归,这是可能的。这更难看,但对于一些用户来说,从中得到一些沉重的东西运行时(不应在运行时中)编译时间可能是值得的。对于某些人来说,增加的丑陋可能不值得牺牲代码的清洁度,所以这取决于每个人的决定,我只是想证明这是可能的。资源受到严格限制的嵌入式领域可能值得考虑。通过这种递归,您可以描述许多算法,您可以ld可能会像prolog那样做tail+head,一次处理一个条目,或者复制数组,一次更改其中的一个条目(可怜人的以模板为中心的连接,而不更改返回类型的大小)。只是有一个限制,在编译时不会看到循环结束时的“if”条件。因此,典型的算法如下:
template<>
constexpr int getSum<maxLoopIndex-1>() {
return foo<maxLoopIndex-1>();
}
模板
int factorialTemplateSimpler(){
如果(输入<2){
返回1;
}否则{
返回factorialTemplateSimpler()*输入;
}
}
不会编译,因为工具链会一直递减直到终止(可能在1000次递归之后)。要让模板看到结束状态,必须明确如下所示:
template<>
constexpr int getSum<maxLoopIndex-1>() {
return foo<maxLoopIndex-1>();
}
模板
constexpr int foo(){
返回N;
}
const int maxLoopIndex=10;
模板
constexpr int getSum(){
返回foo()+getSum();
}
模板
constexpr int getSum(){
返回0;
}
int main(){
constexpr auto sum=getSum();
回报金额;
}
这就是你想要的,它是用C++14编译的,不知道为什么它也用C++11编译
许多算法都有一个特例来完成与典型算法不同的特殊任务,您必须对其进行单独的实现(因为在实例化时不会看到if)。并且您还必须在某个地方结束循环,因此您必须实现单独的退出案例。为了节省额外的键入,最好将退出案例和特殊案例放在同一索引中,这样您就不必创建重复的实现并增加维护。因此,您必须决定是否最好将其计入inc重排或递减方式。因此,您只需实现组合的特殊情况和退出情况一次。要将其放入上下文中,对于阶乘之类的东西,我将递减,这样0-情况和循环结束将使用相同的代码实现,然后让算法在从深度递归返回时完成工作
如果你没有任何特殊情况,你必须做一个特殊情况,例如在上面的代码中,我知道返回0是安全的,我知道当你数到10,但不包括它,因此我在索引10做了特殊情况,并返回了0
模板
constexpr int getSum(){
返回0;
}
如果你不可能做到这一点,那么你必须实现算法的一个子集(没有递归),但在索引9处停止,如下所示:
template<>
constexpr int getSum<maxLoopIndex-1>() {
return foo<maxLoopIndex-1>();
}
模板
constexpr int getSum(){
返回foo();
}
注意:只要常量变量和等式在编译时,就可以使用它们
完整示例:
模板
constexpr int foo(){
返回N;
}
const int maxLoopIndex=10;
模板
constexpr int getSum(){
返回foo()+getSum();
}
模板
constexpr int getSum(){
返回foo();
}
int main(){
constexpr auto sum=getSum()
template<int N>
constexpr int foo() {
return N;
}
template<int index>
constexpr int getSum() {
return foo<index>() + getSum<index-1>();
}
template<>
constexpr int getSum<0>() {
return foo<0>();
}
int main() {
constexpr auto sum = getSum<10 - 1>(); // loop 0..9
return sum;
}