C++结构体中static constexpr模板函数编译报错原因及解决咨询
编译错误原因解析及不拆分结构体的解决办法
最小示例代码
#include<array> #include<iostream> #include <cstdint> enum Indices : std::uint16_t { kIndex1 = 0, kIndex2 = 1, kNumIndices = 2 }; struct TestStruct { static constexpr std::array<uint32_t, Indices::kNumIndices> myvals{4,7}; template <Indices myIndex> static constexpr std::uint32_t GetValue() { return myvals[myIndex]*5; } static constexpr std::array<std::uint32_t, Indices::kNumIndices> newVals{GetValue<Indices::kIndex1>(), GetValue<Indices::kIndex2>()}; }; int main() { auto myArr = TestStruct::newVals; std::cout<<myArr[Indices::kIndex1]<<" "<<myArr[Indices::kIndex2]<<std::endl; }
报错信息
Test.cpp:23:97: error: ‘static constexpr uint32_t TestStruct::GetValue() [with Indices myIndex = kIndex1; uint32_t = unsigned int]’ used before its definition 23 | static constexpr std::array<uint32_t, Indices::kNumIndices> newVals{GetValue<Indices::kIndex1>(), GetValue<Indices::kIndex2>()};
错误原因
这个报错的核心是类内部静态constexpr成员的初始化规则与函数模板可见性的冲突:
虽然GetValue模板函数在newVals前写了完整定义,但C++中类的定义要到闭合的}才算完成。newVals是constexpr静态成员,它的初始化属于常量表达式求值,此时需要实例化GetValue模板,但编译器认为类尚未完整定义,GetValue的定义还未“生效”——常量表达式要求被调用的函数必须已完成定义,因此判定为“使用前未定义”。
不拆分结构体的解决办法
办法1:将newVals的初始化移到类外部
在类内仅声明newVals,类外完成初始化,此时类已完整定义,GetValue模板可正常实例化:
#include<array> #include<iostream> #include <cstdint> enum Indices : std::uint16_t { kIndex1 = 0, kIndex2 = 1, kNumIndices = 2 }; struct TestStruct { static constexpr std::array<uint32_t, Indices::kNumIndices> myvals{4,7}; template <Indices myIndex> static constexpr std::uint32_t GetValue() { return myvals[myIndex]*5; } // 类内仅声明 static constexpr std::array<std::uint32_t, Indices::kNumIndices> newVals; }; // 类外初始化 constexpr std::array<std::uint32_t, Indices::kNumIndices> TestStruct::newVals{GetValue<Indices::kIndex1>(), GetValue<Indices::kIndex2>()}; int main() { auto myArr = TestStruct::newVals; std::cout<<myArr[Indices::kIndex1]<<" "<<myArr[Indices::kIndex2]<<std::endl; }
办法2:用constexpr lambda延迟求值(C++17及以上)
利用C++17的constexpr lambda特性,把newVals的初始化逻辑包裹在lambda中,让GetValue的实例化延迟到lambda调用时,此时类已足够完整:
#include<array> #include<iostream> #include <cstdint> enum Indices : std::uint16_t { kIndex1 = 0, kIndex2 = 1, kNumIndices = 2 }; struct TestStruct { static constexpr std::array<uint32_t, Indices::kNumIndices> myvals{4,7}; template <Indices myIndex> static constexpr std::uint32_t GetValue() { return myvals[myIndex]*5; } static constexpr std::array<std::uint32_t, Indices::kNumIndices> newVals = []{ return std::array<std::uint32_t, Indices::kNumIndices>{ GetValue<Indices::kIndex1>(), GetValue<Indices::kIndex2>() }; }(); }; int main() { auto myArr = TestStruct::newVals; std::cout<<myArr[Indices::kIndex1]<<" "<<myArr[Indices::kIndex2]<<std::endl; }
办法3:将GetValue改为变量模板(C++14及以上)
如果逻辑允许,把函数模板改成变量模板,变量模板的类内求值规则更宽松:
#include<array> #include<iostream> #include <cstdint> enum Indices : std::uint16_t { kIndex1 = 0, kIndex2 = 1, kNumIndices = 2 }; struct TestStruct { static constexpr std::array<uint32_t, Indices::kNumIndices> myvals{4,7}; template <Indices myIndex> static constexpr std::uint32_t GetValue = myvals[myIndex]*5; static constexpr std::array<std::uint32_t, Indices::kNumIndices> newVals{GetValue<Indices::kIndex1>, GetValue<Indices::kIndex2>}; }; int main() { auto myArr = TestStruct::newVals; std::cout<<myArr[Indices::kIndex1]<<" "<<myArr[Indices::kIndex2]<<std::endl; }
内容的提问来源于stack exchange,提问作者AndreasL
相关产品推荐
相关产品推荐

