首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >C++17静态模板懒惰评估

C++17静态模板懒惰评估
EN

Stack Overflow用户
提问于 2020-12-14 16:34:49
回答 4查看 1.4K关注 0票数 20

考虑到以下例子:

代码语言:javascript
运行
复制
#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模板。但是,编译代码仍然会导致来自该分支的无限循环:

代码语言:javascript
运行
复制
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;
      |                                                          ^~~~~

为什么是这种情况?它是否特定于与模板结构相关的东西?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2020-12-14 16:51:19

我熟悉C++中的惰性计算,并且期望在传递参数<0时,不会计算if语句的第二个分支泛型fibonacci模板。

它不需要被评估。但我们不是在处理评估问题。我们正在处理模板实例化。您使用了fibonacci<n-1>::value,这需要实例化完整的对象类型fibonacci<n-1>。必须检查该类型,以确定它是否有一个成员value,可以在这样的表达式中使用。

实例化类模板会导致其成员的声明被实例化。静态数据成员的声明包含一个初始化器,因此也必须实例化它。因此,我们需要递归地实例化模板。

简单地命名fibonacci<n-1>并不会导致它被实例化(想一想前面的声明)。如果要延迟实例化,则必须以需要定义这些类型(例如访问成员)的方式延迟使用这些类型。

这方面的旧的元编程技巧(与函数式编程非常一致)涉及到助手模板。

代码语言:javascript
运行
复制
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 (并且只访问该类型)。因此,在实际需要之前,没有什么是完全实例化的。

票数 21
EN

Stack Overflow用户

发布于 2020-12-14 16:47:15

您可以使用if constexpr

代码语言:javascript
运行
复制
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;
        }
    }();
};
票数 13
EN

Stack Overflow用户

发布于 2020-12-14 16:52:31

当用fibonacci的某个值实例化n时,还必须编译此实例化中使用的所有表达式。这意味着所使用的任何模板也必须实例化。这是必要的,即使包含模板实例化的表达式从未被计算过。

避免在表达式中实例化模板的唯一方法是根本不编译表达式。这使您可以避免使用不正确的参数实例化模板。

您可以通过使用来自if constexpr的C++17来实现这一点:

代码语言:javascript
运行
复制
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;
    }();
};

这是一个演示

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65292785

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档