C++ constexpr变量非字面类型错误排查(含if constexpr场景)
问题分析与修复方案
环境搭建
我定义了两个模板类ContainerA和ContainerB,代码如下:
template <class... T> class ContainerA{ public: constexpr explicit ContainerA(T... data) : data_(data...) {} constexpr explicit ContainerA(std::tuple<T...> data) : data_(data) {} constexpr std::tuple<T...> getDataA() const { return data_; } constexpr std::size_t getSize() const { return sizeof...(T); } protected: const std::tuple<T...> data_; }; template <class T> class ContainerB{ public: constexpr explicit ContainerB(T data) : data_(data) {} constexpr T getDataB() const { return data_; } protected: const T data_; };
同时定义了一个继承自上述类的子类:
template <class O, class... I> class BiggerContainer : public ContainerA<I...>, public ContainerB<O> { public: constexpr explicit BiggerContainer (O dataB, I... dataA) : ContainerA<I...>(dataA...), ContainerB<O>(dataB) {} constexpr explicit BiggerContainer (O dataB, std::tuple<I...> dataA) : ContainerA<I...>(dataA), ContainerB<O>(dataB) {} };
当前正常运行的代码
首先,我初始化了一个std::tuple,其中包含上述每个类的实例:
constexpr auto tup = std::make_tuple(ContainerA(1,2,3), ContainerB(0), BiggerContainer(0,1,2,3));
接下来,我希望通过if constexpr在编译时判断tuple中指定位置存储的是ContainerA、ContainerB还是BiggerContainer,代码如下:
template <class T> struct IsBiggerContainerType: std::false_type {}; template <class... T> struct IsBiggerContainerType<BiggerContainer<T...>> : std::true_type {}; template <class T> constexpr bool isBiggerContainerType(T /*var*/) { return IsBiggerContainerType<T>(); } int main() { constexpr auto element = std::get<2>(tup); if constexpr (isBiggerContainerType(element)) { std::cerr << "Hello World" << std::endl; } }
截至目前,以上代码均可正常运行!
问题描述
现在我新增了一个Test类,它接收类似上述的tuple,并在友元函数中尝试使用if constexpr进行类型检查:
// 假设AbovesClassesConcept已提前定义 template <AbovesClassesConcept... PS> class Test{ public: explicit Test(std::tuple<PS...> data) : data_(data) {} template <size_t idx = 0> friend constexpr void evaluate(); private: const std::tuple<PS...> data_; }; template <size_t idx = 0> constexpr void evaluate() { // ... 一些可正常运行的tuple迭代逻辑 constexpr auto element = std::get<idx>(data_); if constexpr (isBiggerContainerType(element)) { // ... 对element执行操作 } // ... 其他可正常运行的tuple迭代逻辑 }
但当我像这样创建Test实例时:
constexpr auto tup = std::make_tuple(ContainerA(1,2,3), ContainerB(0), BiggerContainer(0,1,2,3)); constexpr Test test(tup);
报错信息:
Constexpr变量不能具有非字面类型'const Test<ContainerA<int, int, int>, ContainerB
, BiggerContainer<int, int, int, int>>'
核心问题与修复步骤
你的问题根源是Test类不满足C++对**字面类型(Literal Type)**的要求,导致无法声明为constexpr变量。具体修复点如下:
1. 构造函数未标记为constexpr
要让类实例在编译期初始化,必须提供constexpr构造函数。原Test构造函数缺少该修饰,无法完成编译期初始化。
修复:
constexpr explicit Test(std::tuple<PS...> data) : data_(data) {}
2. 友元函数evaluate存在语法与逻辑错误
- 原友元声明语法错误:模板友元需要明确关联当前
Test实例,否则函数无法访问私有成员data_。 - 原函数直接访问
data_非法:data_是Test类的私有成员,必须通过实例对象访问。
修复后的友元声明与函数:
template <AbovesClassesConcept... PS> class Test{ public: constexpr explicit Test(std::tuple<PS...> data) : data_(data) {} template <size_t idx = 0> friend constexpr void evaluate(const Test& test); private: const std::tuple<PS...> data_; }; template <size_t idx = 0> constexpr void evaluate(const Test& test) { if constexpr (idx < std::tuple_size_v<decltype(test.data_)>) { constexpr auto element = std::get<idx>(test.data_); if constexpr (isBiggerContainerType(element)) { std::cerr << "Found BiggerContainer at index " << idx << std::endl; } evaluate<idx + 1>(test); } }
3. 确保类满足字面类型的其他要求
Test的所有非静态成员(此处为data_)必须是字面类型:你的ContainerA、ContainerB、BiggerContainer均满足,std::tuple组合后也符合要求。- 类的析构函数必须是平凡的:
Test未自定义析构函数,默认析构函数满足条件。
最终可编译代码示例
#include <tuple> #include <type_traits> #include <iostream> // 定义概念约束 template <class T> concept AbovesClassesConcept = std::is_base_of_v<ContainerA<>, T> || std::is_base_of_v<ContainerB<int>, T> || std::is_base_of_v<BiggerContainer<int, int>, T>; // 此处省略ContainerA、ContainerB、BiggerContainer及类型判断代码(与之前一致) template <AbovesClassesConcept... PS> class Test{ public: constexpr explicit Test(std::tuple<PS...> data) : data_(data) {} template <size_t idx = 0> friend constexpr void evaluate(const Test& test); private: const std::tuple<PS...> data_; }; template <size_t idx = 0> constexpr void evaluate(const Test& test) { if constexpr (idx < std::tuple_size_v<decltype(test.data_)>) { constexpr auto element = std::get<idx>(test.data_); if constexpr (isBiggerContainerType(element)) { std::cerr << "Found BiggerContainer at index " << idx << std::endl; } evaluate<idx + 1>(test); } } int main() { constexpr auto tup = std::make_tuple(ContainerA(1,2,3), ContainerB(0), BiggerContainer(0,1,2,3)); constexpr Test test(tup); evaluate(test); }
内容的提问来源于stack exchange,提问作者fakl
相关产品推荐
相关产品推荐

