解决方案:__cplusplus宏的值始终为199711L(即 C++98)
作者:翟天保Steven
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处
问题现象:被 "冻结" 的__cplusplus宏
在使用 Visual Studio(MSVC)编译 C++ 代码时,许多开发者会遇到一个困惑:无论项目设置的 C++ 标准是 C++11、C++17 还是更高版本,__cplusplus预定义宏的值始终为199711L(即 C++98 标准)。例如,以下测试代码的输出会始终显示 "C++98/03":
#include <iostream>
int main()
{std::cout << "__cplusplus = " << __cplusplus << std::endl;#if __cplusplus >= 201103Lstd::cout << "检测到C++11或更高版本" << std::endl;#elsestd::cout << "检测到C++98/03" << std::endl;#endifreturn 0;
}
这种现象会导致依赖__cplusplus宏进行标准检测的代码无法正确执行,例如:
- 第三方库需要根据不同 C++ 标准选择实现方式时。
- 自定义工具需要判断当前编译环境的标准支持时。
原因分析:MSVC 的兼容性设计
历史兼容性考量
在 Visual Studio 2015 及之后的版本中,MSVC 编译器默认不更新__cplusplus宏的值,主要出于以下考虑:
- 旧代码兼容性:大量旧项目依赖__cplusplus == 199711L的判断逻辑,直接修改宏值可能导致编译错误。
- 标准演进过渡:C++ 标准迭代频繁,MSVC 选择通过其他方式(如_MSVC_LANG宏)标识自身特性,而非修改标准宏。
宏值与实际标准的分离
MSVC 中__cplusplus宏的行为特点:
- 默认值固定:无论项目设置的 C++ 标准如何,__cplusplus始终为199711L。
- 实际标准生效:尽管宏值未变,编译器会正确支持所选 C++ 标准的特性。
- 替代标识:MSVC 提供_MSVC_LANG宏标识自身支持的标准。
解决方案:启用 / Zc:__cplusplus选项
核心方案:激活标准宏更新
MSVC 从 Visual Studio 2017 15.7 版本开始提供/Zc:__cplusplus编译选项,用于正确设置__cplusplus宏的值:
// 启用/Zc:__cplusplus后,__cplusplus将正确反映所选标准
#include <iostream>
int main()
{std::cout << "__cplusplus = " << __cplusplus << std::endl;#if __cplusplus >= 201103Lstd::cout << "C++11或更高版本已激活" << std::endl;#elsestd::cout << "仍为C++98/03模式" << std::endl;#endifreturn 0;
}
不同场景下的配置方法
场景 1:Visual Studio 项目属性设置
- 右键点击项目 → 属性。
- 导航至 配置属性 > C/C++ > 命令行。
- 在 附加选项 中添加 /Zc:__cplusplus。
- 点击 应用和确定 保存设置。
场景 2:CMake 项目配置
在CMakeLists.txt中添加以下配置:
# 方法一:为所有C++编译添加选项
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus")# 方法二:为特定目标添加选项
target_compile_options(your_target PRIVATE /Zc:__cplusplus)
场景 3:命令行直接编译
使用cl.exe命令时显式指定选项:
cl.exe /Zc:__cplusplus /std:c++17 your_code.cpp /Fe:output.exe
实战案例:让标准检测代码正确工作
案例 1:第三方库的标准适配
假设存在一个库需要根据 C++ 标准选择不同的内存分配策略:
// 原始代码(在MSVC中会错误选择C++98路径)
#include <iostream>
void* allocateMemory(size_t size)
{#if __cplusplus >= 201103Lstd::cout << "使用C++11的std::align_alloc" << std::endl;return std::align_alloc(alignof(std::max_align_t), size);#elsestd::cout << "使用C++98的malloc" << std::endl;return std::malloc(size);#endif
}
int main() {allocateMemory(1024);return 0;
}
启用 / Zc:__cplusplus 后的效果:
- 当项目设置为 C++11 时,__cplusplus变为201103L,正确选择 C++11 路径。
- 当项目设置为 C++17 时,__cplusplus变为201703L,同样正确。
案例 2:条件编译的版本检测
检测编译器是否支持 C++20 特性:
#include <iostream>
int main()
{#if __cplusplus >= 202002Lstd::cout << "编译器支持C++20" << std::endl;// 使用C++20特性#elif __cplusplus >= 201703Lstd::cout << "编译器支持C++17" << std::endl;// 使用C++17特性#elsestd::cout << "编译器仅支持C++11或更低" << std::endl;#endifreturn 0;
}
注意事项与边界情况
1. 兼容性风险
启用/Zc:__cplusplus可能导致依赖旧__cplusplus值的代码编译失败,例如:
// 旧代码可能依赖__cplusplus == 199711L
#if __cplusplus == 199711L#include "old_api.h"
#else#include "new_api.h"
#endif
解决方案:结合_MSVC_LANG宏进行双重判断:
#if __cplusplus >= 201103L || defined(_MSVC_LANG) && _MSVC_LANG >= 1700#include "new_api.h"
#else#include "old_api.h"
#endif
2. 版本支持要求
- 必须使用 Visual Studio 2017 15.7 或更高版本。
- 低版本 MSVC(如 VS2015)不支持/Zc:__cplusplus选项。
3. 与其他编译选项的配合
- 该选项需要与/std:c++11、/std:c++17等标准选项配合使用。
- 例如:cl.exe /Zc:__cplusplus /std:c++17 your_code.cpp。
4. 验证设置是否生效
通过以下代码验证__cplusplus是否正确更新:
#include <iostream>
int main()
{std::cout << "__cplusplus = " << __cplusplus << std::endl;#if defined(_MSVC_LANG)std::cout << "_MSVC_LANG = " << _MSVC_LANG << std::endl;#endifreturn 0;
}
总结:让__cplusplus 回归本质
__cplusplus宏作为 C++ 标准的 "身份标识",在 MSVC 中的默认行为曾因兼容性妥协而偏离本质。通过/Zc:__cplusplus选项,开发者可以解锁其正确功能,使代码中的标准检测逻辑与实际编译环境保持一致。
在实际开发中,建议:
- 对新项目默认启用/Zc:__cplusplus。
- 对旧项目进行兼容性评估后逐步启用。
- 结合_MSVC_LANG宏处理复杂的版本兼容场景。
通过这一设置,开发者可以更顺畅地使用条件编译适配不同 C++ 标准,提升代码的可维护性和跨平台兼容性。