如何让MSVC在C++20编译期识别CRTP中的static constexpr成员?
CRTP基类访问派生类static constexpr成员的MSVC兼容方案
背景
尝试在CRTP基类的编译期访问派生类的constexpr静态成员,以下是最小可复现代码:
template <class T> struct CRTP { static constexpr int get_value() { return T::value ; } static constexpr int crtp_value = get_value(); }; struct Derived: CRTP<Derived> { static constexpr int value = 2; }; int main() { static_assert(Derived::crtp_value == 2, "Should result in 2"); return 0; }
该代码在GCC、Clang中可正常编译,但在MSVC中失败。MSVC的报错信息如下:
example.cpp <source>(3): error C2039: 'value': is not a member of 'Derived' <source>(7): note: see declaration of 'Derived' <source>(3): note: the template instantiation context (the oldest one first) is <source>(7): note: see reference to class template instantiation 'CRTP<Derived>' being compiled <source>(3): note: while compiling class template member function 'int CRTP<Derived>::get_value(void)' <source>(3): error C2065: 'value': undeclared identifier <source>(4): error C3615: constexpr function 'CRTP<Derived>::get_value' cannot result in a constant expression <source>(3): note: failure was caused by control reaching the end of a constexpr function <source>(12): error C2131: expression did not evaluate to a constant <source>(12): note: failure was caused by a read of an uninitialized symbol <source>(12): note: see usage of 'crtp_value' Compiler returned: 2
原因
MSVC对类模板实例化时的类型完整性检查更严格:当实例化CRTP<Derived>时,Derived类还处于不完全定义状态(因为它继承自CRTP<Derived>,基类的初始化会优先于派生类成员的解析),此时编译器无法找到Derived::value。而GCC和Clang会延迟对T::value的查找,直到Derived类完全定义后再解析。
解决方案
方案1:将静态变量改为constexpr函数
把crtp_value从静态变量改为constexpr成员函数,函数调用会触发延迟查找,直到Derived完全定义:
template <class T> struct CRTP { static constexpr int get_value() { return T::value ; } static constexpr int crtp_value() { return get_value(); } }; struct Derived: CRTP<Derived> { static constexpr int value = 2; }; int main() { static_assert(Derived::crtp_value() == 2, "Should result in 2"); return 0; }
方案2:使用inline constexpr变量(C++17及以上)
将crtp_value声明为inline constexpr,C++17标准允许inline静态变量的初始化延迟到类完全实例化后,MSVC会正确处理这种延迟初始化:
template <class T> struct CRTP { static constexpr int get_value() { return T::value ; } static inline constexpr int crtp_value = get_value(); }; struct Derived: CRTP<Derived> { static constexpr int value = 2; }; int main() { static_assert(Derived::crtp_value == 2, "Should result in 2"); return 0; }
方案3:通过依赖类型的表达式触发延迟查找
修改get_value,用依赖于T的表达式让编译器延迟查找value:
#include <utility> template <class T> struct CRTP { static constexpr int get_value() { return decltype(T::value){}; } static constexpr int crtp_value = get_value(); }; struct Derived: CRTP<Derived> { static constexpr int value = 2; }; int main() { static_assert(Derived::crtp_value == 2, "Should result in 2"); return 0; }
内容的提问来源于stack exchange,提问作者Krupip
相关产品推荐
相关产品推荐

