C++14中如何程序化初始化类的static constexpr std::array成员?
在C++14中程序化初始化类的static constexpr std::array成员
问题背景
尝试为类的static constexpr std::array成员做程序化初始化(实际场景中数组尺寸为模板非类型参数,无法手动逐个元素初始化),但现有两种方法均失效:
- 类内静态constexpr函数初始化:因类成员初始化时函数定义未完成,编译报错
called in a constant expression before its definition is complete - constexpr lambda初始化:C++14中
std::array的operator[]并非constexpr成员函数,无法在lambda的常量表达式上下文中修改元素
以下是简化的问题代码:
#include <array> constexpr std::size_t N = 3; using Mat = std::array<double, N * N>; // 可行:自由constexpr函数可初始化Mat constexpr Mat InitEye() noexcept { Mat TmpEye{0}; for (std::size_t r = 0; r < N; ++r) { for (std::size_t c = 0; c < N; ++c) { TmpEye[r * N + c] = (r == c) ? 1. : 0.; } } return TmpEye; } // 不可行:类内调用未完成定义的静态函数 class Wrapper { private: static constexpr Mat WrappedInitEye() noexcept { Mat TmpEye{0}; for (std::size_t r = 0; r < N; ++r) { for (std::size_t c = 0; c < N; ++c) { TmpEye[r * N + c] = (r == c) ? 1. : 0.; } } return TmpEye; } public: static constexpr Mat Eye = WrappedInitEye(); // 编译报错 }; // 不可行:C++14中std::array无constexpr访问运算符 class Wrapper2 { public: static constexpr Mat Eye = [] { Mat TmpEye{0}; for (std::size_t r = 0; r < N; ++r) { for (std::size_t c = 0; c < N; ++c) { TmpEye[r * N + c] = (r == c) ? 1. : 0.; } } return TmpEye; }(); // C++14下无法通过编译 };
解决方案
方案1:使用类外部的constexpr函数(适配模板场景)
将初始化逻辑放在全局或命名空间级别的constexpr函数中,类内直接引用该函数初始化静态成员。对于模板类,只需将初始化函数也定义为模板函数,匹配模板非类型参数即可:
#include <array> // 模板化初始化函数,适配任意尺寸N template<std::size_t N> constexpr std::array<double, N*N> InitEye() noexcept { std::array<double, N*N> TmpEye{0}; for (std::size_t r = 0; r < N; ++r) { for (std::size_t c = 0; c < N; ++c) { TmpEye[r * N + c] = (r == c) ? 1. : 0.; } } return TmpEye; } template<std::size_t N> class Wrapper { public: static constexpr std::array<double, N*N> Eye = InitEye<N>(); }; // C++14要求:static constexpr成员需在类外完成定义(C++17及以后可省略) template<std::size_t N> constexpr std::array<double, N*N> Wrapper<N>::Eye; int main() { constexpr auto eye = Wrapper<3>::Eye; return 0; }
方案2:类外初始化静态成员与函数
将静态constexpr函数的定义和静态成员的初始化都移到类外,确保初始化时函数已完成定义:
#include <array> constexpr std::size_t N = 3; using Mat = std::array<double, N * N>; class Wrapper { private: // 仅在类内声明静态函数 static constexpr Mat WrappedInitEye() noexcept; public: // 仅在类内声明静态成员 static constexpr Mat Eye; }; // 类外定义静态函数 constexpr Mat Wrapper::WrappedInitEye() noexcept { Mat TmpEye{0}; for (std::size_t r = 0; r < N; ++r) { for (std::size_t c = 0; c < N; ++c) { TmpEye[r * N + c] = (r == c) ? 1. : 0.; } } return TmpEye; } // 类外初始化静态成员,此时函数已完成定义 constexpr Mat Wrapper::Eye = WrappedInitEye(); int main() { constexpr auto eye = Wrapper::Eye; return 0; }
原理说明
- 方案1利用了全局/命名空间级别的函数在类定义前已完成编译的特性,避免了类内成员定义未完成的问题,同时模板化设计完美适配尺寸为模板参数的场景。
- 方案2通过将初始化逻辑移到类外,确保静态成员初始化时,依赖的静态函数已经完成完整定义,符合C++14对常量表达式的要求。
内容的提问来源于stack exchange,提问作者Oersted
相关产品推荐
相关产品推荐

