ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

react-native-windows 内嵌 Folly 的 C++ 类型特性(Type Traits)解析:IsRelocatable、IsOneOf 与 FOLLY_ASSUME 系列宏

2026/9/21 15:15:47 拓冰建站 浏览量
react-native-windows 内嵌 Folly 的 C++ 类型特性(Type Traits)解析:IsRelocatable、IsOneOf 与 FOLLY_ASSUME 系列宏 react-native-windows 内嵌 Folly 的 C 类型特性Type Traits解析IsRelocatable、IsOneOf 与 FOLLY_ASSUME 系列宏【免费下载链接】react-native-windowsA framework for building native Windows apps with React.项目地址: https://gitcode.com/gh_mirrors/re/react-native-windows本文以 react-native-windows 仓库内嵌的 Folly 库官方文档 vnext/external/folly/folly/docs/Traits.md 为核心系统讲解folly::IsRelocatable与folly::IsOneOf两个补充型特性trait的设计动机、语义、用法并结合仓库内 vnext/external/folly/folly/Traits.h 的源码实现与fbvector、small_vector、MPMCQueue等容器对它们的实际消费方式帮助 C 开发者在自己的类型上正确声明可重定位等假设从而解锁 memcpy 级对象搬移优化。读完本文你将掌握什么是可重定位类型、如何用特化或宏向 Folly 声明假设、fbvector为何要求元素可重定位、以及IsOneOf的递归实现原理。一、背景为什么需要type_traits之外的补充特性C 标准库的type_traits提供了大量编译期类型查询工具例如std::is_integral、std::is_floating_point、std::is_same等用于在模板元编程中获取类型的属性。然而标准库并未覆盖所有有用的类型属性其中最重要的一类缺失就是可重定位性relocatability。folly/Traits.h的目标正是补足这些缺口。它实现了两个核心特性folly::IsRelocatableT判断类型T是否可以用memcpy搬移内存folly::IsOneOfT, T1, T2, ..., Tn判断类型T是否与T1至Tn中的某一个完全相同同时提供一组宏FOLLY_ASSUME_RELOCATABLE、FOLLY_ASSUME_FBVECTOR_COMPATIBLE*让开发者用极少的代码声明自己的类型假设。从源码结构看vnext/external/folly/folly/Traits.h 中还承载了大量其他补充特性如type_identity、void_t、detected_or、is_detected_v、conditional_t、type_pack_element_t等本文聚焦文档主题相关的IsRelocatable、IsOneOf及其配套宏。二、IsRelocatable判断类型能否用 memcpy 搬移2.1 语义定义两种搬移代码序列的等价性在 C 中移动一个对象的保守做法是调用拷贝构造在目标地址构造新对象再销毁旧对象。而另一种更激进的优化是直接用memcpy复制对象的原始内存。对于可重定位类型下面两段代码在语义上必须完全等价文档原话见 vnext/external/folly/folly/docs/Traits.md{ void conservativeMove(T * from, T * to) { new(to) T(from); // 在 to 处拷贝构造 (*from).~T(); // 销毁旧对象 } } { void optimizedMove(T * from, T * to) { memcpy(to, from, sizeof(T)); // 直接复制内存 } }也就是说folly::IsRelocatableT::value描述的是类型T的一个值能否仅凭memcpy在内存中搬移见 Traits.h 源码注释。2.2 非可重定位类型的典型例子绝大多数 C 类型都是可重定位的——包括所有基本类型以及由可重定位类型组合而成的类型。不可重定位类型的经典案例是内部含自引用指针的类型。文档给出的示例是在嵌入式缓冲区内部维护指针的类型class NonRelocatableType { private: char buffer[1024]; char * pointerToBuffer; // 指向自身 buffer 的指针 ... public: NonRelocatableType() : pointerToBuffer(buffer) {} ... };如果对这类对象直接memcpy内部指针pointerToBuffer仍指向旧对象的buffer新对象内部将留下悬空指针程序必然出错。因此这类类型必须使用拷贝构造 析构的保守搬移方式。值得注意的边界源码注释中还提到极少数类型需要更新指向自己的远程指针remote pointers to pointers tracking them这类类型同样不可重定位Traits.h 第 571-607 行注释。2.3 源码级实现默认保守可被检测与特化覆盖IsRelocatable的实际实现位于 vnext/external/folly/folly/Traits.h 第 672-684 行template class T struct IsRelocatable : std::conditional !require_sizeofT || is_detected_vtraits_detail::detect_IsRelocatable, T, traits_detail::has_true_IsRelocatableT, #if defined(__cpp_lib_is_trivially_relocatable) // P1144 std::is_trivially_relocatableT #else std::is_trivially_copyableT #endif ::type { };这段实现揭示了三条关键规则检测内嵌 typedef如果类型T内定义了typedef std::true_type IsRelocatable;或typedef std::false_type ...则以此为准。这就是detect_IsRelocatable与has_true_IsRelocatable的作用见第 615-629 行的FOLLY_HAS_TRUE_XXX宏展开。fallback 到标准特性未显式声明时回退到std::is_trivially_relocatableT若编译器支持 C P1144 提案或std::is_trivially_copyableT。默认保守文档明确指出默认假设类型不可重定位只有已声明/可检测的类型才会被认为可重定位。此外源码还提供了第二种声明方式在类的头文件内部、与类定义同处写typedef std::true_type IsRelocatable;Traits.h 第 602-604 行注释并且可以在基类中通过覆盖为std::false_type来取消继承的假设。2.4 容器如何消费 IsRelocatableIsRelocatable最大的受益者是高性能容器。仓库内的证据包括vnext/external/folly/folly/container/FBVector.h 是重度使用者当folly::IsRelocatableT::value usingStdAllocator时元素搬移走 memcpy 快路径第 623、647、680、689、1214、1255、1353 行的if (folly::IsRelocatableT::value usingStdAllocator)分支。vnext/external/folly/folly/container/small_vector.h 同样在if constexpr (IsRelocatableValue::value)时选择更快的搬移策略第 544、612 行并在第 1054 行通过static_assert(IsRelocatableValue::value)强制要求元素可重定位。vnext/external/folly/folly/MPMCQueue.h 在元素类型满足IsRelocatableT::value时同样利用该属性做搬移优化第 705、1390-1393、1442 行。三、向 Folly 声明假设特化与宏3.1 手动特化 IsRelocatable文档给出的标准做法是在类型定义之后、全局命名空间不在任何 namespace 内为folly::IsRelocatable编写全特化或偏特化/* 假设已有如下类型定义 */ template class T1, class T2 class MyParameterizedType; class MySimpleType; /* 全局命名空间非任何 namespace 内部 */ namespace folly { // 为普通类型 MySimpleType 定义特化 template struct IsRelocatableMySimpleType : std::true_type {}; // 为模板类型 MyParameterizedTypeT1, T2 定义偏特化 template class T1, class T2 struct IsRelocatableMyParameterizedTypeT1, T2 : ::std::true_type {}; }源码注释Traits.h 第 596-600 行特别提醒务必把IsRelocatableSomeStruct的特化放在与SomeStruct同一个头文件中以保证任何包含该类型定义的地方都能同时看到特化。3.2 用 FOLLY_ASSUME_RELOCATABLE 宏简化声明为免去手写template struct IsRelocatable... : std::true_type {};的样板代码folly/Traits.h在命名空间folly内提供了宏FOLLY_ASSUME_RELOCATABLETraits.h 第 816-817 行#define FOLLY_ASSUME_RELOCATABLE(...) \ struct IsRelocatable__VA_ARGS__ : std::true_type {}用法必须位于namespace folly作用域内namespace folly { // 普通类型 FOLLY_ASSUME_RELOCATABLE(MySimpleType); // 参数化类型需在前面补 template 参数声明 template class T1, class T2 FOLLY_ASSUME_RELOCATABLE(MyParameterizedTypeT1, T2); }3.3 fbvector 的硬性要求与静态断言fbvector只接受可重定位的元素。如果类型没有显式声明假设直接使用fbvectorMySimpleType或fbvectorMyParameterizedType会因下列断言编译失败文档原文同时在 small_vector.h 第 1054 行也有同类static_assertstatic_assert(IsRelocatableMy*Type::value, );3.4 FOLLY_ASSUME_FBVECTOR_COMPATIBLE*一步声明可重定位 无异常默认构造FOLLY_ASSUME_FBVECTOR_COMPATIBLE*(type)系列宏一次完成两件事声明该类型可重定位relocatable且具有无异常默认构造函数nothrow constructor。这些宏必须用在全局作用域任何命名空间之外其内部会打开namespace folly并生成对应的特化宏定义见 Traits.h 第 844-872 行。宏适用类型展开后的特化形式FOLLY_ASSUME_FBVECTOR_COMPATIBLE(MyType)普通类型template FOLLY_ASSUME_RELOCATABLE(MyType);FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(MyType)1 个模板参数的类型族template class T1 FOLLY_ASSUME_RELOCATABLE(MyTypeT1);FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(MyType)2 个模板参数的类型族template class T1, class T2 FOLLY_ASSUME_RELOCATABLE(MyTypeT1, T2);FOLLY_ASSUME_FBVECTOR_COMPATIBLE_3(MyType)3 个模板参数的类型族template class T1, class T2, class T3 FOLLY_ASSUME_RELOCATABLE(MyTypeT1, T2, T3);FOLLY_ASSUME_FBVECTOR_COMPATIBLE_4(MyType)4 个模板参数的类型族template class T1, class T2, class T3, class T4 FOLLY_ASSUME_RELOCATABLE(MyTypeT1, T2, T3, T4);注意_1至_4版本传入的是模板名字本身如MyParameterizedType而不是实例化后的类型。3.5 仓库内的真实使用范例在 vnext/external/folly/folly/container/test/FBVectorTest.cpp 第 93-105 行测试定义了一个自定义类型UserDefinedType随后在全局作用域写FOLLY_ASSUME_FBVECTOR_COMPATIBLE(UserDefinedType)紧接着就能在fbvectorUserDefinedType中安全使用TEST(fbvector, worksWithUserDefinedType)。这是该宏最直接的实战示范。vnext/external/folly/folly/FBString.h 第 1012 行使用内嵌typedef std::true_type IsRelocatable;的方式声明自身可重定位。vnext/external/folly/folly/MPMCQueue.h 第 89-91 行注释专门提醒如果类型没有noexcept标记但被声明为folly::IsRelocatable则可用FOLLY_ASSUME_FBVECTOR_COMPATIBLE来声明以配合队列的搬移优化。3.6 预置兼容的标准库类型一批常见标准库类型已被 Folly 预先实例化并声明为与fbvector兼容可以直接使用而无需任何额外声明文档明确列举std::basic_string、std::vector、std::list、std::map、std::deque、std::set、std::unique_ptr、std::shared_ptr、std::function。关于智能指针源码在 Traits.h 第 973-977 行有更细的实现非 MSVC 编译器下通过FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(std::unique_ptr)与FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(std::shared_ptr)声明而 MSVC 下不做任何假设// Assume nothing when compiling with MSVC.。此外还有两条组合规则std::pairT, U只要T、U都可重定位就安全地认为std::pairT, U可重定位。这一点在源码中有精确实现Traits.h 第 877-879 行template class T, class U struct IsRelocatablestd::pairT, U : std::bool_constantIsRelocatableT::value IsRelocatableU::value {};四、IsOneOf多类型判等特性std::is_sameT1, T2::value只能比较两个类型是否相同。当需要判断类型T是否等于一组类型中的某一个时folly::IsOneOf派上用场folly::IsOneOfT, T1, Ts...::value即若T与T1, T2, ..., Tn中任意一个完全相同则value true否则为false。文档说明该特性通过递归实现。仓库源码给出了现代实现Traits.h 第 882-883 行template typename T, typename... Ts using IsOneOf StrictDisjunctionstd::is_sameT, Ts...;它基于StrictDisjunction第 753-756 行定义——一个急切求值版本的逻辑或逐个展开std::is_sameT, Ts只要有一个为真即整体为真。这与文档描述的递归语义一致且具有 O(1) 的模板实例化深度是比手写递归更高效的等价实现。五、在 react-native-windows 中的定位与使用建议react-native-windows 在 vnext/external 目录下内嵌了完整的 Folly 源码含folly/docs/Traits.md文档、folly/Traits.h头文件以及folly/container/FBVector.h、folly/container/test/FBVectorTest.cpp等配套文件作为其 C 基础组件库使用。这意味着阅读与理解IsRelocatable、IsOneOf时可以直接在本仓库内对照 vnext/external/folly/folly/docs/Traits.md、vnext/external/folly/folly/Traits.h 与各容器实现无需访问外部资源。如果自定义的 C 类型需要放入fbvector、small_vector、MPMCQueue等容器并享受 memcpy 搬移优化请遵循本文第三章的三种声明途径之一内嵌 typedef、folly命名空间内特化、全局作用域宏并牢记特化与类型定义同头文件的约定。声明假设是有责任的只有确认类型内部不含自引用指针、也不依赖拷贝构造/析构的副作用才能声明为可重定位。文档与源码均强调默认保守不可重定位错误声明将导致难以排查的内存错误。六、小结folly::IsRelocatableT::value表达能否用 memcpy 搬移T的值默认保守为假可通内嵌 typedef、folly命名空间特化、FOLLY_ASSUME_RELOCATABLE宏三种方式声明为真。FOLLY_ASSUME_FBVECTOR_COMPATIBLE*系列宏在全局作用域使用一步声明可重定位 无异常默认构造_1至_4后缀对应模板参数个数。fbvector、small_vector、MPMCQueue会在元素可重定位时走 memcpy 快路径fbvector对未声明类型以static_assert强制拦截。folly::IsOneOfT, Ts...判断多类型判等源码以StrictDisjunctionstd::is_sameT, Ts...实现文档所述的递归语义。以上全部结论均可在本仓库 vnext/external/folly/folly/docs/Traits.md 及其对应源码、测试文件中逐一验证适合作为 react-native-windows 及其内嵌 Folly 库的 C 开发参考。【免费下载链接】react-native-windowsA framework for building native Windows apps with React.项目地址: https://gitcode.com/gh_mirrors/re/react-native-windows创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考