loki库编译失败主因是头文件包含顺序错误、c++标准过高及使用非官方代码;须按typelist.h→nulltype.h→typemanip.h顺序包含,用-std=c++11,并取自sourceforge原始包。

loki库编译失败:找不到Typelist或HierarchicalVisitor
loki是Andrei Alexandrescu在《Modern C++ Design》中提出的模板元编程实践库,不是现代C++生态里“开箱即用”的第三方库。它依赖大量非标准模板技巧(比如偏特化深度嵌套、空基类优化手动模拟),在GCC 7+、Clang 10+或MSVC 2019之后默认会报错。
常见错误现象:error: 'Typelist' was not declared in this scope 或链接时undefined reference to 'Loki::Private::TypeListImpl::... —— 实际上是因为头文件没全包含,或宏定义顺序不对。
- 必须按顺序包含:
#include <loki></loki>→#include <loki></loki>→#include <loki></loki>,漏掉NullType.h会导致Typelist无法终止递归 - 禁用C++17及以后的模板解析严格模式:编译时加
-std=c++11(不能用c++14及以上),否则TYPELIST_4等宏会展开失败 - 所有loki头文件需从原始SourceForge仓库获取(
loki-0.1.7.tar.gz),GitHub上那些“modernized”分支基本不可用——它们重写了SmallObj但删掉了Visitor核心逻辑
用Visitor模式绕过虚函数:为什么Accept要写两遍
loki的HierarchicalVisitor本质是编译期双分派,靠模板推导类型而非运行时vtable。你必须在被访问类里显式写Accept,又在访问者里写对应Visit重载——这不是冗余,而是为了触发SFINAE匹配。
典型场景:需要对Shape派生类(Circle、Rect)做不同渲染,但不想为每个子类加虚函数,也不愿用dynamic_cast。
-
Circle类里必须有:template<class t> void Accept(T& visitor) { visitor.Visit(*this); }</class>—— 注意参数是T&,不是Visitor& - 访问者类要继承
Loki::BaseVisitor,并声明void Visit(Circle&)和void Visit(Rect&);漏掉任一Visit,编译器不会报错,但调用时静默跳过该类型 - 性能影响:无虚调用开销,但每个
Accept实例化一次模板,对象体积略增;若派生类超10个,编译时间明显变长
SmallObj分配器踩坑:析构顺序错乱导致double free
loki的SmallObjAllocator专为小对象(SmallObj就自动解决析构问题,结果在容器里存裸指针时崩溃。
错误现象:malloc(): double free detected in tcache 2,通常发生在std::vector<widget></widget>析构时,而Widget是用SmallObjAllocator::Allocate分配的。
-
SmallObjAllocator不记录分配时的类型信息,Deallocate必须传入**原始分配大小**(不是sizeof(T)),否则内存池索引错位 —— 建议封装成template<class t> T* Alloc() { return static_cast<t>(alloc_.Allocate(sizeof(T))); }</t></class> - 绝不能混用:
new分配的对象不能交由SmallObjAllocator::Deallocate释放,反之亦然;跨线程使用前必须加锁(loki本身不提供线程安全) - 兼容性注意:Windows下
HeapFree对齐要求更严,若SmallObjAllocator的ChunkSize设为非2的幂(如128),可能触发STATUS_ACCESS_VIOLATION
andrei的设计思想怎么落地:别直接抄Functor或Singleton
《Modern C++ Design》的价值不在代码复用,而在展示“如何把设计模式编译期化”。loki里Functor用Typelist推导参数,Singleton用Creator模板解耦构造策略——但这些在C++11后基本被std::function和std::call_once替代了。
真正该学的是思路:比如用Policy-Based Design组合功能。你想给日志模块加缓冲、压缩、加密三个可选能力,就该仿LOKI_DEFAULT_POLICY写自己的BufferingPolicy、GzipPolicy,而不是硬塞进一个类。
- 避免照搬
Singleton:它的Longevity机制依赖全局对象析构顺序,在DLL边界或跨静态库时极易出错;现在优先用static local variable + std::call_once - 别强套
Functor:C++11的lambda捕获列表比loki的Functor<int></int>更直观,除非你要在编译期决定调用签名(比如DSL解析器) - 最易忽略的点:
Policy类必须满足“无状态”或“仅含编译期常量”,否则多个模板实例会生成重复符号;检查方法:把policy类的成员全改成static constexpr再编译
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











