UE4 Mac构建编译报错 no template named “is_void_v” in namespace “std”

发布于:2025-09-06 ⋅ 阅读:(18) ⋅ 点赞:(0)

背景

UE4 Mac构建编译报错 no template named “is_void_v” in namespace “std” ,但WINDOWS没有遇到错误。

分析过程

1、is_void_v 是否属于C++17

从语法上看,AI说不只是 C++17 才有。早在C++14 就有 is_void_v 的用法了。


2、WINDOWS中is_void_v的定义

这段代码在WINDOWS下定义在
C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.40.33807\include\type_traits


3、Mac中is_void_v的定义

在WINDOWS中,is_void_v 的定义是在: C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.40.33807\include\type_traits ,那么在Mac机器中,它可能定义在哪个路径下?
我找到了它在Mac机器上的定义,位于:/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__type_traits/is_void.h ,具体定义是:

//代码1:
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP___TYPE_TRAITS_IS_VOID_H
#define _LIBCPP___TYPE_TRAITS_IS_VOID_H

#include <__config>
#include <__type_traits/integral_constant.h>
#include <__type_traits/is_same.h>
#include <__type_traits/remove_cv.h>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#  pragma GCC system_header
#endif

_LIBCPP_BEGIN_NAMESPACE_STD

#if __has_builtin(__is_void)

template <class _Tp>
struct _LIBCPP_TEMPLATE_VIS is_void : _BoolConstant<__is_void(_Tp)> {};

#  if _LIBCPP_STD_VER >= 17
template <class _Tp>
inline constexpr bool is_void_v = __is_void(_Tp);
#  endif

#else

template <class _Tp>
struct _LIBCPP_TEMPLATE_VIS is_void : public is_same<__remove_cv_t<_Tp>, void> {};

#  if _LIBCPP_STD_VER >= 17
template <class _Tp>
inline constexpr bool is_void_v = is_void<_Tp>::value;
#  endif

#endif // __has_builtin(__is_void)

_LIBCPP_END_NAMESPACE_STD

#endif // _LIBCPP___TYPE_TRAITS_IS_VOID_H


4、_LIBCPP_STD_VER的含义

_LIBCPP_STD_VER是 ​libc++(LLVM 的 C++ 标准库实现)内部用来表示其当前所支持的 C++ 标准版本的一个宏。它的值是一个整数,对应了不同的 C++ 标准发布年份。
这个宏的主要作用是让 libc++ 的代码能够根据不同的 C++ 标准版本条件地编译某些特性或提供特定版本的 API。以下是 _LIBCPP_STD_VER常见值及其对应的 C++ 标准的梳理:

#  if _LIBCPP_STD_VER >= 17
template <class _Tp>
inline constexpr bool is_void_v = __is_void(_Tp); // 或者 = is_void<_Tp>::value;
#  endif

上述代码意味着 ​is_void_v这个变量模板只有在 C++17 或更高版本下才会被定义和提供。这很好地解释了你最初的问题:is_void_v确实是 C++17 引入的特性。

5、理解 _LIBCPP_STD_VER与 __cplusplus的区别

你可能会想到另一个预定义宏 __cplusplus,它由编译器直接定义,用于指示编译器所遵循的 C++ 标准版本。

  • ​__cplusplus​:由 ​编译器​ 定义,表示编译器当前正在编译所遵循的 C++ 标准模式。它的值也是年份,例如 C++17 模式下其值通常为 201703L。
  • ​_LIBCPP_STD_VER​:由 ​libc++ 标准库头文件​ 定义,表示 ​libc++ 库本身实现所支持的最高或当前选择的 C++ 标准版本。

简单来说:

  • __cplusplus告诉你编译器在用什么模式。
  • _LIBCPP_STD_VER告诉你标准库在用什么模式。

在大多数正常配置下,_LIBCPP_STD_VER的值会和 __cplusplus的值保持一致(经过某种映射,例如 __cplusplus == 201703L时 _LIBCPP_STD_VER被定义为 17),因为它们需要协同工作。这个映射逻辑通常隐藏在 libc++ 的内部配置头文件(如 <__config>)中。

6、Mac中决定_LIBCPP_STD_VER 的地方
//代码2:
public CppStandardVersion CppStandard = CppStandardVersion.Default;
static string GetCppStandardCompileArgument(CppCompileEnvironment CompileEnvironment)
{
    var Mapping = new Dictionary<CppStandardVersion, string>
    {
       { CppStandardVersion.Cpp14, " -std=c++14" },
       { CppStandardVersion.Cpp17, " -std=c++17" },
       { CppStandardVersion.Latest, " -std=c++17" },
       { CppStandardVersion.Default, " -std=c++14" }
    };
    return Mapping[CompileEnvironment.CppStandard];
}

在我的构建机中,该默认配置,读取的就是c++14,也就是Mac机用的也就是c++14.

7、WINDOWS是否是 c++14 ?

是的,在我的构建机中,WINDOWS 也是c++14。论述如下。在UE4中,我看到下面变量决定了c++的标准:

public class ModuleRules 
{
    …… 
    public CppStandardVersion CppStandard = CppStandardVersion.Default;
    ……
}

然而在WINDOWS的构建机中,我观察到UBT产生的编译命令中,没有 -std=c++14 或 -std=c++17 或 /std:c++latest 或 /Qstd=c++14 或 /std:c++17 或 /std:c++14 中的任何一个。我调试的代码位于 UnrealBuildTool.VCToolChain.AppendCLArguments_CPP() ,UnrealBuildTool.VCToolChain里的 ​​"VC"​​ 字面上指的是 ​Visual C++​,这是微软(Microsoft)提供的 C++ 编译工具链 。最终观察到下面这些参数,它们记录在EngineSource\Engine\Intermediate\Build\Win64\UE4Editor\Development\Launch\Module.Launch.cpp.obj.response :

/Zc:inline

/nologo

/Oi

/c

/Gw

/Gy

/Zm1000

/wd4819

/D_CRT_STDIO_LEGACY_WIDE_SPECIFIERS=1

/D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS=1

/D_DISABLE_EXTENDED_ALIGNED_STORAGE

/source-charset:utf-8

/execution-charset:utf-8

/Ox

/Ot

/GF

/Ob2

/errorReport:prompt

/EHsc

/Z7

/MD

/bigobj

/fp:fast

/Zo

/Zp8

/we4456

/we4458

/we4459

/wd4463

/wd4838

/we4668

也就是说,没有显式声明 C++版本。那么,WINDOWS下UE4最终会以什么C++标准来编译呢?

在 Windows 下观察到的 UE4 编译命令中没有显式指定 C++ 标准(如 /std:c++17),这通常意味着它依赖于编译器的默认标准。结合你提到的 ModuleRules中的 CppStandard = CppStandardVersion.Default设置,分析如下:

8、总结

在Mac构建机和WINDOWS构建机中,构建机编译器都正在用着C++14,而非C++17 。

前面我们知道,mac中 要用 C++17,才能编译到 is_void_v,但 为什么WINDOWS用 c++14 却也能编译 is_void_v ?

因为,在 WINDOWS的定义( C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.40.33807\include\type_traits )中,没有C++版本限制;

_EXPORT_STD template <class _Ty>
_INLINE_VAR constexpr bool is_void_v = is_same_v<remove_cv_t<_Ty>, void>;

_EXPORT_STD template <class _Ty>
struct is_void : bool_constant<is_void_v<_Ty>> {};

而在 Mac的定义( /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__type_traits/is_void.h )中,有C++版本限制;

#  if _LIBCPP_STD_VER >= 17
template <class _Tp>
inline constexpr bool is_void_v = __is_void(_Tp);
#  endif

修复方法

因此,我们将所有的 std::([^<]+)_v< 都替换为 std::($1)::value ,例如:


网站公告

今日签到

点亮在社区的每一天
去签到