C++中实现类Lisp的编译期constexpr cons链表
编译期Lisp风格Cons链表的C++实现
我一眼就发现你代码里的核心问题啦——你的pair结构体还没实现编译期可访问的car/cdr接口,而且缺少Lisp里必不可少的**空列表(nil)**终结类型,也没体现出cons链表递归嵌套的模板结构。咱们一步步把它补全,实现一个纯编译期的cons链表:
第一步:定义空列表标记与基础cons节点
首先需要一个专门的类型表示空列表,然后完善pair(也就是cons节点)的模板,确保所有操作都是constexpr的:
#include <type_traits> #include <iostream> namespace chops { // 空列表的标记类型,对应Lisp里的nil struct nil_t {}; constexpr nil_t nil; // 基础cons节点模板,E1是当前元素,E2是后续链表(可以是另一个pair或者nil_t) template <typename E1, typename E2> struct pair { constexpr pair(const E1& car, const E2& cdr) : _car{car}, _cdr{cdr} {} // 编译期访问car和cdr constexpr const E1& car() const noexcept { return _car; } constexpr const E2& cdr() const noexcept { return _cdr; } private: E1 _car; E2 _cdr; }; // 辅助函数,简化cons节点的创建(类似Lisp的cons函数) template <typename E1, typename E2> constexpr auto cons(const E1& car, const E2& cdr) { return pair<E1, E2>{car, cdr}; } } // namespace chops
第二步:编译期操作的实现
有了基础结构,咱们可以实现一些编译期的链表操作,比如计算链表长度:
namespace chops { // 编译期计算链表长度的递归模板 template <typename List> constexpr auto length(const List&) { if constexpr (std::is_same_v<List, nil_t>) { return 0; } else { return 1 + length(List{}.cdr()); } } // 编译期遍历链表(运行时打印,也可以改成编译期生成值) template <typename List> void print(const List& list) { if constexpr (std::is_same_v<List, nil_t>) { std::cout << "nil\n"; } else { std::cout << list.car() << " -> "; print(list.cdr()); } } } // namespace chops
第三步:使用示例
现在你可以在编译期创建链表,甚至让编译器计算链表长度:
int main() { // 编译期创建链表: 1 -> 2 -> 3 -> nil constexpr auto my_list = chops::cons(1, chops::cons(2, chops::cons(3, chops::nil))); // 编译期计算长度,结果是3 constexpr auto len = chops::length(my_list); static_assert(len == 3, "Length should be 3"); // 运行时打印链表 chops::print(my_list); // 输出: 1 -> 2 -> 3 -> nil return 0; }
关键细节说明
- 空列表的处理:用
nil_t作为链表的终结类型,通过std::is_same_v在编译期判断是否到达链表末尾 - constexpr的严格性:所有构造函数、访问器、辅助函数都标记为
constexpr,确保可以在编译期执行 - 模板递归:链表的结构是递归的
pair<Elem, pair<...>>,直到最后一个节点的cdr是nil_t
内容的提问来源于stack exchange,提问作者meguli
相关产品推荐
相关产品推荐

