背景
我的工程在编译iOS时,遇到如下报错:
2025-09-05 11:43:51:940 : /Users/bkdevops/_ios1/EngineSource/Engine/Source/Runtime/Core/Public/Containers/Array.h:46:53: error: no member named 'disjunction' in namespace 'std'
分析
由于WINDOWS的构包没有这个报错,因此先观察其(disjunction)定义。WINDOWS下定义在 C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.40.33807\include\xtr1common ,如代码1,Mac下的定义在
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__type_traits/disjunction.h ,如代码2。
//代码1:
_EXPORT_STD template <class... _Traits>
struct disjunction : false_type {}; // If _Traits is empty, false_type
template <class _First, class... _Rest>
struct disjunction<_First, _Rest...> : _Disjunction<_First::value, _First, _Rest...>::type {
// the first true trait in _Traits, or the last trait if none are true
};
//代码2:
// _Or always performs lazy evaluation of its arguments.
//
// However, `_Or<_Pred...>` itself will evaluate its result immediately (without having to
// be instantiated) since it is an alias, unlike `disjunction<_Pred...>`, which is a struct.
// If you want to defer the evaluation of `_Or<_Pred...>` itself, use `_Lazy<_Or, _Pred...>`
// or `disjunction<_Pred...>` directly.
template <class... _Args>
using _Or _LIBCPP_NODEBUG = typename _OrImpl<sizeof...(_Args) != 0>::template _Result<false_type, _Args...>;
#if _LIBCPP_STD_VER >= 17
template <class... _Args>
struct disjunction : _Or<_Args...> {};
template <class... _Args>
inline constexpr bool disjunction_v = _Or<_Args...>::value;
#endif // _LIBCPP_STD_VER >= 17
由于我的Mac构建机没有配置为 _LIBCPP_STD_VER 17 ,具体原因见我的下一篇博文(UE4 Mac构建编译报错 no template named "is_void_v" in namespace "std"-CSDN博客)。因此 disjunction 未定义。
最终方案
在UE4的使用了disjunction 的地方,改为下面形式:
#if PLATFORM_IOS
constexpr bool TArrayElementsAreCompatible_V = std::_Or<std::is_same<DestType, std::decay_t<SourceType>>, std::is_constructible<DestType, SourceType>>::value;
#else
constexpr bool TArrayElementsAreCompatible_V = std::disjunction<std::is_same<DestType, std::decay_t<SourceType>>, std::is_constructible<DestType, SourceType>>::value;
#endif
disjunction的含义
针对代码2,下面这个表格汇总了代码中关键部分及其含义:
代码片段 | 含义 |
---|---|
#if _LIBCPP_STD_VER >= 17 |
条件编译预处理指令 :检查 libc++ 的配置宏 _LIBCPP_STD_VER 是否表示支持 C++17 或更高版本。 |
_LIBCPP_STD_VER |
libc++ 内部宏 :其整数值代表 libc++ 目标遵循的 C++ 标准版本 ,例如 17 代表 C++17,20 代表 C++20。 |
template <class... _Args> struct disjunction : _Or<_Args...> {}; |
**std::disjunction 的主模板定义** :它继承自一个内部实现 _Or 。 |
std::disjunction |
C++17 标准引入的模板 :对多个类型特性 (type traits) 进行逻辑或 (OR) 运算的元函数。 |
template <class... _Args> inline constexpr bool disjunction_v = _Or<_Args...>::value; |
C++17 标准引入的变量模板 :是 disjunction<_Args...>::value 的简写,方便使用。 |
#endif |
结束条件编译块。 |
🔧 **std::disjunction
的功能与原理**
std::disjunction
是一个模板元函数 ,它接受任意数量(变长模板参数 class... _Args
)的类型特性(例如 std::is_integral<T>
, std::is_floating_point<T>
等),并计算它们的逻辑或。
-
运算规则 :相当于
T1::value || T2::value || ... || Tn::value
,但是在编译期进行的。 -
短路求值 :这是
disjunction
一个非常重要的特性。如果在模板参数包_Args...
中,某个类型特性Bi
的value
静态成员为true
,那么disjunction
的value
就是true
,并且编译器会停止实例化后续的类型特性 (即不会去计算Bj::value
,其中j > i
)。这在后续的类型实例化代价高昂或可能导致编译错误时特别有用。 -
继承关系 :
std::disjunction<B1, B2, ..., BN>
会公开继承自第一个value
为true
的Bi
类型。如果所有Bi::value
都为false
,则继承自最后一个BN
。这意味着你不仅可以获取最终的::value
,有时还可以获取到符合条件的那个特性类型本身的信息。
💡 简单示例
#include <type_traits>
#include <iostream>
int main() {
// 检查类型 T 是否是整型或浮点型之一
using T = double;
if (std::disjunction_v<std::is_integral<T>, std::is_floating_point<T>>) {
std::cout << "T is either integral or floating point.\n";
} else {
std::cout << "T is neither integral nor floating point.\n";
}
return 0;
}
在这个例子中,std::disjunction_v<>
会在编译期计算 std::is_integral<double>::value || std::is_floating_point<double>::value
。由于 double
是浮点类型,第二个特性的 value
为 true
,因此整个 disjunction_v
的结果为 true
。