禁用特定MSVC版本的优化
Disable optimization on specific MSVC version
我有一个使用CMake的项目,由于编译器错误(模板解析,优化和异常处理之间的交互),无法在VS 2015上编译此代码。
这个bug可以通过禁用优化来避免——虽然这会产生次优的代码,但至少项目可以编译。
如何在Release
和RelWithDebInfo
构建中将MSVC 2015的默认优化级别更改为/O0
?
我的naïve实现将是CMAKE_CXX_FLAGS
中的条件替换-未来安全吗?
另一种方法是在违规的标题中使用#pragma
。
这个编译器错误使链接器与垃圾一起烦恼,尽我所能追踪问题(VS2015是新的,在撰写本文时没有发布补丁/更新)。崩溃发生在链接器中。编译器似乎认为一切顺利。
当这个bug被"激发"时,问题在使用生成的模板对象的代码中。换句话说,即使您在标题中禁用了优化,但在正文中重新启用了优化,它仍然会使链接器崩溃。"有效"的是禁用对实例化和使用模板对象的成员函数的代码的优化(您可以对该目标之外的所有代码保持优化)。
例如,在问题中发布的代码中,在整个头中保持优化。对于使用模板的函数,执行:
#pragma optimize( "", off )
void test()
{
two<one<int> > obj;
obj.func();
}
#pragma optimize( "", on )
这隔离了对引发问题的代码的优化损失,并且链接器成功。
当然,pragma本身可以用条件定义或其他机制包装,一旦VS2015的补丁发布修复了这个问题,你可以禁用这些机制。
通过这种方式,代码可以在不考虑构建配置的情况下使用(意思是,它将适用于CMAKE构建和IDE构建),而不必给代码的后续用户带来负担(除了定义来控制是否禁用优化)。
如果它恰好适合你的情况,你也可以尝试这样做:
template<typename T>
struct two
{
T t;
void func()
{
typedef typename T::type type;
type i = std::numeric_limits<type>::min();
type j = std::numeric_limits<type>::epsilon();
t.t1 = i / 2 + j;
t.t2 = t.t1;
}
};
这种重写代码绕过了这个错误,并且编译时不会导致链接器崩溃。
同样,这也绕过了这个问题:
void func()
{
typedef typename T::type type;
t.t1 = std::numeric_limits<type>::min() / 2 + std::numeric_limits<type>::epsilon();
t.t2 = t.t1;
}
而且,这对我来说很奇怪,并且代表了稍微更好的设计(因为它不需要一个类来摆弄另一个类的成员)
template<typename T>
struct one
{
typedef T type;
T t1, t2;
void set( const T & i ) { t1 = t2 = i; }
};
template<typename T>
struct two
{
T t;
void func()
{
typedef typename T::type type;
t.set( std::numeric_limits<type>::min() / 2 + std::numeric_limits<type>::epsilon() );
}
};
好消息是,有一个解决方案不涉及改变项目构建规则,使优化生效,并有效地相同。
看来罪魁祸首实际上是t.t1 = t.t2 = ...
子句。为什么这样的赋值会触发链接器崩溃,而等价表达式不会,这是微软要深入研究的一个谜,但实际上,解决方案似乎会导致代码看起来稍微好一些。
这将只在Release和VS 2015中隔离#pragma
#if ( NDEBUG && _MSC_VER == 1900 )
#pragma optimize ("d", on) // same as /Od
#endif
只是为了补充使用基于c++的变通方法的答案,这里是一个使用CMake的CHECK_CXX_SOURCE_COMPILES()
宏的基于CMake的实现示例:
cmake_minimum_required(VERSION 3.2)
include(CheckCXXSourceCompiles)
project(TestCompile CXX)
if (MSVC AND MSVC_VERSION GREATER 1899)
set(CMAKE_TRY_COMPILE_CONFIGURATION "Release")
CHECK_CXX_SOURCE_COMPILES(
"
#include <limits>
template<typename T>
struct one
{
typedef T type;
T t1, t2;
};
template<typename T>
struct two
{
T t;
void func()
{
typedef typename T::type type;
t.t1 = t.t2 = std::numeric_limits<type>::min() / 2 + std::numeric_limits<type>::epsilon();
}
};
void main()
{
two<one<int> > obj;
obj.func();
}
"
CAN_LINK_MY_TEMPLATES_EXAMPLE
)
if (NOT CAN_LINK_MY_TEMPLATES_EXAMPLE)
add_compile_options("/Od")
endif()
endif()
我认为你不必改变特定构建配置的标志,用add_compile_options("/Od")
一般禁用它就足够了。
如果你想在每个配置中这样做,你可以替换/删除CMAKE_CXX_FLAGS_...
变量中的/O2
标志(如这里或这里)。
- 空基优化子对象的地址
- 为cl.exe(Visual Studio代码)指定命令行C++版本
- 导入库可以跨dll版本工作吗
- 关闭||运算符优化
- 如何解决gcc编译器优化导致的centos双编译器设置中的分段错误
- 在调用FreeLibrary后,释放动态链接到具有相同版本的CRT堆的DLL的内存
- 在clang++预处理器中确定gcc工具链版本
- 返回值优化:显式移动还是隐式
- 码头化的C++应用程序是否向后兼容早期的内核版本
- 人脸跟踪arduino代码的优化
- 使用仅使用一次的变量调用的复制构造函数.这可能是通过调用move构造函数进行编译器优化的情况吗
- 不同的Visual Studio版本中缺少.dll
- 用符号版本替换对函数的所有调用
- luaL_dofile在已知良好的字节码上失败,可以使用未编译的版本
- 哪个版本(调试/发行 - 未取代/优化)更好地研究C 与组件进行比较
- c++ 编译器优化是否可以针对布尔参数生成两个版本的函数
- 为调试版本启用仅标头库的优化
- VisualStudio 优化编译器版本 19.00.23506.0 中存在明显的编译错误
- 调试优化版本是否会导致程序的行为不同
- 禁用特定MSVC版本的优化