如何构造可赋值给constexpr变量的含tuple向量的C++23类?
你想要构造一个能在编译期实例化、包含tuple向量的类,但现有代码在C++23下编译失败。以下是你的最小可复现代码:
#include <vector> #include <tuple> template<typename... Ts> class vector_of_tuple { public: constexpr vector_of_tuple( const std::tuple<Ts...> & value ) : samples( 1, value ) {}; private: std::vector<std::tuple<Ts...>> samples; }; constexpr std::tuple<int,long,int> tuple_v = std::make_tuple(1,2,3); // 添加constexpr修饰时编译报错 constexpr vector_of_tuple<int,long,int> vector_of_tuple_v(tuple_v);
编译报错信息:
constexpr变量‘vector_of_tuple_v’的类型‘const vector_of_tuple<int, long int, int>’不是字面类型
‘vector_of_tuple<int, long int, int>’不是字面类型的原因:
非静态数据成员‘vector_of_tuple<int, long int, int>::samples’具有非字面类型
‘std::vector<std::tuple<int, long int, int>, std::allocator<std::tuple<int, long int, int> > >’不是字面类型的原因:
‘std::vector<std::tuple<int, long int, int>, std::allocator<std::tuple<int, long int, int> > >’没有constexpr析构函数
问题根源
虽然C23为std::vector添加了大量constexpr成员函数(比如构造、元素访问、修改等),但**std::vector的析构函数直到C26才被标记为constexpr。而constexpr变量要求其类型是字面类型**,字面类型的核心条件之一是类必须拥有constexpr析构函数——这就是代码编译失败的核心原因。
解决方案
1. 改用std::array(固定大小场景)
如果编译期能确定容器的元素数量,std::array是完全符合字面类型要求的替代品,支持所有constexpr操作:
#include <array> #include <tuple> template<typename... Ts, std::size_t N = 1> class array_of_tuple { public: constexpr array_of_tuple(const std::tuple<Ts...>& value) : samples{value} {} private: std::array<std::tuple<Ts...>, N> samples; }; constexpr std::tuple<int, long, int> tuple_v = std::make_tuple(1,2,3); constexpr array_of_tuple<int, long, int> array_of_tuple_v(tuple_v);
2. 自定义编译期动态容器(可变大小场景)
如果需要编译期动态添加元素,可以自己实现一个基于固定缓冲区的constexpr容器,用std::array作为底层存储,跟踪实际元素数量:
#include <tuple> #include <array> template<typename... Ts, std::size_t MaxSize = 16> class constexpr_tuple_vector { public: constexpr constexpr_tuple_vector(const std::tuple<Ts...>& value) { push_back(value); } constexpr void push_back(const std::tuple<Ts...>& value) { // 超出最大容量时可触发编译错误,避免运行期问题 static_assert(MaxSize > 0, "Max size must be positive"); if (size_ >= MaxSize) { throw std::out_of_range("Exceed max container size"); } data_[size_++] = value; } constexpr std::size_t size() const noexcept { return size_; } constexpr const std::tuple<Ts...>& operator[](std::size_t idx) const noexcept { return data_[idx]; } private: std::array<std::tuple<Ts...>, MaxSize> data_; std::size_t size_ = 0; }; constexpr std::tuple<int, long, int> tuple_v = std::make_tuple(1,2,3); constexpr constexpr_tuple_vector<int, long, int> vec_v(tuple_v);
3. 升级到C++26标准
如果项目可以切换到C26标准,原代码无需修改即可正常编译运行——因为C26正式将std::vector的析构函数标记为constexpr,使其成为字面类型。
内容的提问来源于stack exchange,提问作者SRH

