CRTP基类如何访问派生类暴露的非类型模板参数?
CRTP基类访问派生类非类型模板参数的问题
问题场景
尝试用CRTP(奇异递归模板模式)让基类Base访问派生类Derived的非类型模板参数N时,VS2022编译报错。报错代码如下:
template <typename TDerived> struct Base { int array[TDerived::NValue]; }; template <int N> struct Derived : Base<Derived<N>> { constexpr static int NValue = N; }; int main() { Derived<8> derived; }
错误信息:'NValue': is not a member of 'Derived<8>'
疑问:是否必须用单独的特质结构体?另外,下面的代码能编译,但额外传递的N显得冗余:
template <typename TDerived, int N> struct Base { int array[N]; }; template <int N> struct Derived : Base<Derived<N>, N> { }; int main() { Derived<8> derived; }
解决方法
不需要额外的特质结构体,也能避免冗余参数,以下是几种简便方案:
方案1:C++11+用decltype延迟解析
利用decltype的延迟检查特性,让数组大小的推导等到派生类完全定义后再进行:
template <typename TDerived> struct Base { int array[decltype(TDerived::NValue)]; }; template <int N> struct Derived : Base<Derived<N>> { constexpr static int NValue = N; }; int main() { Derived<8> derived; }
原理是decltype会触发SFINAE延迟检查,直到Derived的完整定义对编译器可见时,才去解析TDerived::NValue,避开了基类实例化时派生类未完全定义的问题。
方案2:模板特化(兼容C++03)
如果需要兼容更早的C++标准,可以通过基类的模板特化直接提取派生类的模板参数:
// 提前声明Derived模板 template <int N> struct Derived; // 通用基类模板 template <typename TDerived> struct Base; // 针对Derived<N>的基类特化 template <int N> struct Base<Derived<N>> { int array[N]; }; template <int N> struct Derived : Base<Derived<N>> { constexpr static int NValue = N; }; int main() { Derived<8> derived; }
这种方式直接从Derived<N>的模板参数中获取N,不需要依赖派生类的静态成员,也没有冗余参数。
方案3:C++20+用延迟初始化的静态成员
C++20允许静态数据成员的初始化延迟到类模板实例化的合适时机,因此可以直接在基类中定义依赖派生类成员的静态变量:
template <typename TDerived> struct Base { static constexpr auto array_size = TDerived::NValue; int array[array_size]; }; template <int N> struct Derived : Base<Derived<N>> { constexpr static int NValue = N; }; int main() { Derived<8> derived; }
此时array_size的初始化会等到Derived完全定义后执行,能正确获取NValue的值。
总结
不需要使用单独的特质结构体,通过上述方法就能在基类中安全访问派生类的非类型模板参数,同时避免冗余的参数传递。
内容的提问来源于stack exchange,提问作者pic32cpp
相关产品推荐
相关产品推荐

