/external(外部标头诊断)

/external 编译器选项允许你为某些头文件指定编译器诊断行为。 “外部”头文件是“只是我的代码”的自然补充:你不能或不打算更改的头文件,例如系统文件或第三方库文件。 由于你不打算更改这些文件,因此你可能会认为从编译器中查看有关它们的诊断消息没有用处。 /external 编译器选项使你可以控制这些警告。

从 Visual Studio 2017 版本 15.6 开始提供 /external 编译器选项。 在 Visual Studio 2019 版本 16.10 之前的 Visual Studio 版本中,/external 选项还需要你设置 /experimental:external 编译器选项。

语法

使用外部标头选项(在 16.10 及更高版本中不需要):

/experimental:external

指定外部标头:

/external:anglebrackets
/external:env:var
/external:I path

指定诊断行为:

/external:W0
/external:W1
/external:W2
/external:W3
/external:W4
/external:templates-

自变量

/experimental:external
启用外部标头选项。 Visual Studio 2019 版本 16.10 及更高版本不需要此选项。

/external:anglebrackets
#include <header> 包含的所有标头(其中 header 文件包含在尖括号 (< >) 中)视为外部标头。

/external:I path
定义包含外部标头的根目录。 path 的所有递归子目录都被认为是外部的,但只有 path 值被添加到编译器搜索 include 文件的目录列表中。 /external:Ipath 之间的空格是可选的。 包含空格的目录必须用双引号引起来。 目录可以是绝对路径或相对路径。

/external:env:var
指定保存以分号分隔的外部头目录列表的环境变量 var 的名称。 它对于依赖于环境变量(例如 INCLUDE)的生成系统很有用,你可以使用它来指定外部 include 文件的列表。 或者,对于不应由 /analyze 分析的文件,则为 CAExcludePath。 例如,你可以指定 /external:env:INCLUDE 以使 INCLUDE 中的每个目录立即成为外部头目录。 这与使用 /external:I 指定单个目录相同,但不那么冗长。 var/external:env: 之间不应有空格。

/external:Wn
此选项将外部标头的默认警告级别设置为 n(0 到 4 之间的值)。 例如,/external:W0 将有效地关闭外部标头的警告。 如果未指定此选项,编译器会针对其他 /external 选项发出命令行警告 D9007。 这些选项被忽略,因为它们没有效果。

/external:Wn 选项的效果类似于将包含的标头包装在 #pragma warning 指令中:

#pragma warning (push, 0)
// the global warning level is now 0 here
#include <external_header>
#pragma warning (pop)

/external:templates-
允许在代码中实例化的模板中出现来自外部标头的警告。

注解

默认情况下,你为构建指定的 /Wn 警告级别适用于所有文件。 指定外部标头的选项仅定义一组文件,你可以对其应用不同的默认警告级别。 因此,如果你指定外部头文件,还可以使用 /external:Wn 指定外部警告级别来更改编译器行为。

所有现有的启用、禁用和抑制警告的机制在外部和非外部文件中仍然有效。 例如,warning pragma 仍然可以覆盖你为外部标头设置的默认警告级别。

示例:设置外部警告级别

这个示例程序有两个源文件,分别为 program.cppheader_file.hheader_file.h 文件位于包含 program.cpp 文件的目录的 include_dir 子目录中:

源文件 include_dir/header_file.h

// External header: include_dir/header_file.h

template <typename T>
struct sample_struct
{
    static const T value = -7; // W4: warning C4245: 'initializing':
    // conversion from 'int' to 'unsigned int', signed/unsigned mismatch
};

源文件 program.cpp

// User code: program.cpp
#include <header_file.h>

int main()
{
    return sample_struct<unsigned int>().value;
}

可以使用以下命令行生成示例:

cl /EHsc /I include_dir /W4 program.cpp

正如预期的那样,此示例会生成警告:

program.cpp
include_dir\header_file.h(6): warning C4245: 'initializing': conversion from 'int' to 'const T', signed/unsigned mismatch
        with
        [
            T=unsigned int
        ]
program.cpp(6): note: see reference to class template instantiation 'sample_struct<unsigned int>' being compiled

要将头文件视为外部文件并禁止显示警告,你可以改用命令行 *

cl /EHsc /I include_dir /external:anglebrackets /external:W0 /W4 program.cpp

此命令行抑制 header_file.h 中的警告,同时保留 program.cpp 中的警告。

跨越内部和外部边界的警告

为外部标头设置低警告级别可以隐藏一些可操作的警告。 特别是,它可以关闭用户代码中模板实例化时发出的警告。 这些警告可能表明你的代码中存在仅在特定类型的实例化中发生的问题。 (例如,如果你忘记应用类型特征删除 const&。)为了避免在外部标头中定义的模板内消除警告,你可以使用 /external:templates- 选项。 编译器会考虑定义模板的文件中的有效警告级别和模板实例化发生的警告级别。 如果模板在非外部代码中实例化,则会出现在外部模板内发出的警告。 例如,此命令行在示例代码 * 中重新启用来自模板源的警告:

cl /EHsc /I include_dir /external:anglebrackets /external:W0 /external:templates- /W4 program.cpp

C4245 警告再次出现在输出中,即使模板代码位于外部标头中。

启用、禁用或抑制警告

所有现有的启用、禁用和抑制警告的机制在外部标头中仍然有效。 由于使用 /external:templates- 选项而出现警告时,你仍然可以在实例化时抑制警告。 例如,要显式抑制样本中由于 /external:templates- 而重新出现的警告,可使用 warning pragma 指令:

int main()
{
    #pragma warning( suppress : 4245)
    return sample_struct<unsigned int>().value;
}

库编写者可以使用相同的机制来强制实施某些警告,或者某些级别的所有警告(如果编写者认为觉得这些警告不应该被 /external:Wn 静默)。 比如这个版本的头文件强制警告 C4245 报错:

// External header: include_dir/header_file.h

#pragma warning( push, 4 )
#pragma warning( error : 4245 )

template <typename T>
struct sample_struct
{
    static const T value = -7; // W4: warning C4245: 'initializing': conversion from 'int'
                               // to 'unsigned int', signed/unsigned mismatch
};

#pragma warning( pop )

通过对库标头进行此更改,库的作者确保此标头中的全局警告级别为 4,无论在 /external:Wn 中指定了什么。 现在将报告所有级别 4 及以上的警告。 库作者还可以强制将某些警告视为错误,可以禁用、抑制或仅在标头中发出一次。 /external 选项不会覆盖那个深思熟虑的选择。

system_header 杂注

#pragma system_header 是一种侵入性标记,允许库编写者将某些标头标记为外部。 包含 #pragma system_header 的文件从 pragma 点到文件末尾被视为外部文件,就好像它在命令行上被指定为外部一样。 编译器在 pragma 之后以 /external:Wn 指定的警告级别发出任何诊断信息。 有关详细信息,请参阅 system_header pragma

限制

编译器的后端代码生成发出的一些警告不受 /external 选项的影响。 这些警告通常以 C47XX 开头,但并非所有 C47XX 警告都是后端警告。 你仍然可以使用 /wd47XX 单独禁用这些警告。 代码分析警告也不受影响,因为它们没有警告级别。

在 Visual Studio 开发环境中设置此编译器选项

在 Visual Studio 2019 版本 16.10 及更高版本中:

  1. 打开项目的“属性页” 对话框。 有关详细信息,请参阅在 Visual Studio 中设置 C++ 编译器和生成属性

  2. 选择“配置属性”>“VC++ 目录”属性页

  3. 设置“外部包含目录”属性以指定每个以分号分隔的路径的 /external:I path 选项的 IDE 等效项。

  4. 选择“配置属性”>“C/C++”>“外部包含”属性页。

  5. 设置属性:

    • 将“将角括号中包含的文件视为外部文件”设置为“是”可设置 /external:anglebrackets 选项。

    • “外部标头警告级别”允许你设置 /external:Wn 选项。 如果将此值设置为“继承项目警告级别”或默认值,则会忽略其他 /external 选项。

    • 将“外部标头中的模板诊断”设置为“是”以设置 /external:templates- 选项。

  6. 选择“确定”或“应用”以保存更改。

在 Visual Studio 2019 版本 16.10 之前的 Visual Studio 版本中:

  1. 打开项目的“属性页” 对话框。 有关详细信息,请参阅在 Visual Studio 中设置 C++ 编译器和生成属性

  2. 选择“配置属性”>“C/C++”>“命令行”属性页

  3. 在“其他选项”框中输入 /experimental:external 选项和其他 /external 编译器选项。

  4. 选择“确定”或“应用”以保存更改。

以编程方式设置此编译器选项

* 在 Visual Studio 2019 版本 16.10 之前的 Visual Studio 版本中添加用于启用外部标头选项的 /experimental:external 选项。

另请参阅

MSVC 编译器选项
MSVC 编译器命令行语法