使用openMVS库,在VS2022中启用c++17标准编译仍然报错
现象
项目中引用了某些开源库(例如openmvs2.1.0
),编译时要求启用编译器对c++17
的支持。
没问题!大家都知道在下图所示的位置调整C++语言标准:
但是,打开开关之后编译,依然报错:
这可真是一个令人百思不得其解的错误啊。报错的文件是官方的头文件type_traits
,双击之后跳转的错误位置也看不出个所以然。
这下头大了。不过,之前编译openmvs库时,用的是同一版本的VS编译器,怎么就成功了呢?于是查看vcpkg自动编译openmvs时用cmake配置生成的CMakeCache.txt中的编译选项,才找到了真正原因,如下图所示:
图中标出来的选项是cmake生成的,并不常见,至少在VS2022中没有提供开关,而只能靠用户手动输入到属性页、C/C++、命令行
的其他选项框中。
报错原因:__cplusplus
宏
__cplusplus
宏是MSVC编译器预定义的宏,它的值是许多语法特性的开关。前面讲了,要启用c++17标准,就在C++语言标准
设置好即可。然而,__cplusplus
宏值不随上面的C++语言标准
(/std 选项
)设置而改变,需要同时添加:/Zc:__cplusplus
编译选项,才能随着语言标准而改变,如下表所示:
/Zc:__cplusplus 选项 | /std 选项 | __cplusplus 值 |
---|---|---|
Zc:__cplusplus | /std:c++14(默认值) | 201402L |
Zc:__cplusplus | /std:c++17 | 201703L |
Zc:__cplusplus | /std:c++20 | 202002L |
Zc:__cplusplus | /std:c++latest | 当前最新日期,目前是202002L |
Zc:__cplusplus-(已禁用) | 任何值 | 199711L |
未指定 | 任何值 | 199711L |
表中的/std 选项
就是上一节的C++语言标准
属性。
可见,如果没有添加/Zc:__cplusplus
,__cplusplus
宏的值始终都是199711L,启用的c++17标准也是不完整的。微软这么操作的理由是:有很多老代码写得不规范(不符合语言标准),之前版本的编译器(由于对语言标准的遵守不严格)能编译通过,我们希望使用新编译器时它们的改动也尽可能小。
于是,本来支持严格的标准C++语法的编译器的默认状态仍和原来一样,__cplusplus
宏的值始终是199711L
;直到开发者在编译器的命令行中加入/Zc:__cplusplus
,它才真正变成实际使用语言标准的值。
其实多数情况下,__cplusplus
宏的值对代码编译影响不大。但是很多高水平开源库使用了新语法,恰恰需要__cplusplus
宏的值符合语言标准(/std 选项
)才能编译通过,这是对开源开发者很不友好的。
P.S. 官方的愿景是,有朝一日仅需要设定
/std 选项
,__cplusplus
宏的值就随之改变,不需要额外添加/Zc:__cplusplus
。
解决方法
在属性页—C/C++—命令行—其他选项
中添加:/Zc:__cplusplus
。重新编译,即可。
扩展阅读
/Zc-cplusplus的微软官方说明
C++语言标准切换选项
官方:终于能正确设定__cplusplus值了
用/std选项确定c/c++语言标准
强制符合语法标准选项/permissive-
一点认识:MSVC的C/C++编译器默认情况下实现了当前标准和未来一些标准中的语法。当指定语言标准为默认的/std:c++14
时,大部分新标准的语法将被禁用,但仍有一部分在引入/std选项之前就实现的新语法标准可用。
但指定/std:c++xx
(xx>14)时,新实现的c++xx以后的语法将不可用。