如何编写C++ Concept以检查类型是否为结构类型?
用C++ Concept检查类型是否为结构类型(可作为非类型模板参数)
我想写一个C++ Concept,用来判断某个类型是不是结构类型——也就是可以作为模板非类型参数的类型。对于自带默认构造函数的类型,检查逻辑很简单,但由于requires参数不能声明为constexpr,我没法实现通用场景的检查。
示例代码如下:
struct StructuralType1 { constexpr StructuralType1(int a=42): a(a) {} int a; }; struct StructuralType2 { constexpr StructuralType2(int a): a(a) {} int a; }; struct NonStructuralType { constexpr NonStructuralType(int a): a(a) {} private: int a; }; template <auto T> consteval void is_structural_helper() {}; template<typename T> concept is_structural = requires () { {is_structural_helper<T{}>() }; // 依赖默认构造函数实现检查 }; // 这两个检查符合预期 static_assert(is_structural<StructuralType1>, "BUG! StructuralType1 is not structural"); static_assert(!is_structural<NonStructuralType>, "BUG! NonStructuralType is structural"); // 这个检查失败,但StructuralType2其实是结构类型(只是没有默认构造) //static_assert(is_structural<StructuralType2>, "BUG! StructuralType2 is not structural"); template <auto X> constexpr int increment() { return X.a+1; } // 证明StructuralType2可以作为非类型模板参数 constexpr int fourtytwo = increment<StructuralType2{41}>();
现有实现的问题很明显:is_structural依赖T{}构造实例,这要求类型必须支持默认构造。像StructuralType2这种没有默认构造、但确实符合结构类型要求的类型,会被错误判定为非结构类型。
解决方案1:利用编译器内置扩展(GCC/Clang)
GCC和Clang提供了内置的类型判断__is_structural_type,可以直接用来实现这个Concept,完全不依赖构造函数:
template<typename T> concept is_structural = __is_structural_type(T);
修改后,所有结构类型(包括StructuralType2)都能被正确识别,三个static_assert都能正常通过。
解决方案2:标准C的近似实现(C20)
如果要写不依赖编译器扩展的代码,在C++20里很难做到完全通用——因为标准没有提供直接判断结构类型的Trait,且无法在requires表达式中构造任意constexpr实例。不过可以针对结构类型的标准定义,做部分近似检查:
结构类型的类需要满足:
- 所有非静态数据成员都是公有的
- 没有用户声明的析构函数
- 没有虚函数
- 没有私有/受保护的基类
- 没有虚基类
- 所有非静态数据成员也都是结构类型(递归检查)
以下是简化版的实现,用SFINAE检查成员访问性和基础类型特征:
#include <type_traits> // 检查是否能访问T的非静态数据成员(这里以成员a为例,实际需要递归检查所有成员) template<typename T, typename = void> struct has_public_data_members : std::false_type {}; template<typename T> struct has_public_data_members<T, std::void_t<decltype(std::declval<T>().*&T::a)>> : std::true_type {}; // 基础结构类型检查 template<typename T> concept is_structural = std::is_class_v<T> && !std::has_virtual_destructor_v<T> && !std::is_polymorphic_v<T> && has_public_data_members<T>;
注意:这个实现是简化版,无法递归检查所有成员类型,也无法覆盖所有边缘情况,只能作为近似方案。
内容的提问来源于stack exchange,提问作者Alessandro M
相关产品推荐
相关产品推荐

