C++17 CRTP问题:在基类中使用派生类的别名模板
CRTP实现范畴论Functor接口的编译错误修复方案
问题背景
要实现一个用于演示范畴论原理的结构体接口,要求包含两个静态成员:
- 别名模板
F<T>:表示函子作用的目标类型 - 成员函数模板
fmap:实现函子的映射操作
尝试用CRTP(奇异递归模板模式)约束该接口时,编译报错“Vector中无成员F”,原因是CRTP基类会在派生类完全定义前被实例化,此时基类无法访问派生类中定义的嵌套别名模板F<T>。
修复方案
核心思路是延迟基类对派生类嵌套成员的解析时机,让相关类型的解析推迟到派生类完全定义后(即基类的成员函数被实际调用/实例化时)。以下是具体修复代码:
修复后的代码示例
#include <vector> #include <algorithm> #include <type_traits> #include <utility> // CRTP基类:延迟对Derived嵌套成员的解析 template <typename Derived> struct Functor { // fmap的参数类型依赖于Derived的F<A>,该类型会在函数被实例化时才解析 template <typename Func, typename A> static auto fmap(Func&& func, const typename Derived::template F<A>& arg) { // 直接转发到派生类的fmap实现 return Derived::fmap(std::forward<Func>(func), arg); } }; // 派生类:实现具体的函子接口 struct Vector : Functor<Vector> { // 定义别名模板F<T> template <typename T> using F = std::vector<T>; // 实现fmap:支持任意可调用对象 template <typename Func, typename A> static auto fmap(Func&& func, const F<A>& vec) { // 推导映射后的元素类型 using B = std::invoke_result_t<Func, const A&>; F<B> res; std::transform(vec.begin(), vec.end(), std::back_inserter(res), std::forward<Func>(func)); return res; } }; // 测试代码 int main() { Vector::F<int> nums = {1,2,3}; auto doubled = Vector::fmap([](int x){ return x*2; }, nums); return 0; }
修复原理
- 基类
Functor中的typename Derived::template F<A>属于依赖类型,编译器不会在基类实例化时立即解析,而是等到fmap函数被实际调用(即派生类完全定义后)才会去查找Derived中的F<A>别名。 - 派生类的
fmap使用std::invoke_result_t自动推导返回类型,避免了硬编码类型,同时让接口更灵活。
内容的提问来源于stack exchange,提问作者Timtro
相关产品推荐
相关产品推荐

