Visual C++ 2005 编译器中的重大更改

更新:2007 年 11 月

本主题讨论 Visual C++ 2005 中的行为更改,这些更改可导致早期版本中的有效代码无法被编译,或在运行时表现出不同的行为。

有关新功能的更多信息,请参见 Visual C++ 2005 和早期版本中的更改Visual C++ 2005 库中的更改Visual C++ 2005 编译器、语言和工具中的更改

  • 指向成员的指针现在需要限定名和 &
    针对仅使用方法名的以前版本的编译器编写的代码现在提供编译器错误 C3867编译器警告 C4867。此诊断是标准 C++ 所必需的。现在,若要创建指向成员函数的指针,必须使用取地址运算符 (&) 以及方法的完全限定名。如果不要求使用 & 运算符和方法的完全限定名,则由于函数调用中缺少括号,可能会导致代码中出现逻辑 Bug。在没有参数列表的情况下使用函数的名称会导致一个函数指针可转换为几种类型。此代码将已编译,从而导致在运行时出现意外行为。

  • 友元声明必须能够访问类
    对于 Visual C++ 2005 以前的 Visual C++ 编译器而言,如果某个类在包含友元声明的类的范围内不可访问,则编译器将允许该友元声明访问该类。现在,编译器将提供编译器错误 C2248。若要解决此错误,请更改在友元声明中指定的类的可访问性。此更改是为了符合 C++ 标准。

  • __int asm 3 现在编译为本机形式
    /clr 进行编译时,__asm int 3 不会导致生成本机代码;编译器将指令转换为 CLR break 指令。在 Visual C++ 2005 中,__asm int 3 现在将导致为函数生成本机代码。如果想要函数导致在代码中出现断点,并且您想要该函数编译为 MSIL,请使用 __debugbreak。有关更多信息,请参见__asm/clr(公共语言运行库编译)。进行此更改是为了更加明确何时生成本机代码,以及何时生成托管代码;内联程序集代码应生成本机代码。

  • 不允许将显式专用化作为复制构造函数/复制赋值运算符
    依赖于复制构造函数或复制赋值运算符的显式模板专用化的代码现在将收到编译器错误 C2299。标准 C++ 禁止这一点。此更改是出于一致性方面的考虑,其目的是提高代码可移植性。

  • 不能将非专用化的类模板用作基类列表中的模板参数
    在类定义的基类列表中使用非专用化的模板类名将导致编译器错误 C3203。将非专用化的模板类名用作基类列表中的模板参数是非法的。当要将某个模板类型参数用作基类列表中的模板参数时,请显式地将该模板类型参数添加到模板类名中。此更改是出于一致性方面的考虑,其目的是提高代码可移植性。

  • 不再允许嵌套类型的 using 声明
    具有嵌套类型的 using 声明的代码现在将生成编译器错误 C2885。若要解决此错误,请完全限定对嵌套类型的引用,将类型放入命名空间中,或者创建 typedef。此更改是出于一致性方面的考虑,其目的是提高代码可移植性。

  • 依据 /clr:oldSyntax,编译器不允许 const_cast 向下强制转换
    在 Visual C++ 2005 之前,Visual C++ 编译器允许在编译源代码时 const_cast Operator向下强制转换,该源代码使用 C++ 托管扩展语法。使用 const_cast 执行向下强制转换现在将导致编译器错误 C2440。若要解决此错误,请使用正确的强制转换运算符(有关更多信息,请参见Casting Operators)。此更改是出于一致性方面的考虑。

  • 编译器不允许托管枚举的前向声明
    在 Visual C++ 2005 之前,Visual C++ 编译器允许托管枚举的前向声明。 现在,如果在用任何形式的 /clr 进行编译时声明并未定义托管枚举,将会导致编译器错误 C2599。若要解决此错误,请始终在声明时定义托管枚举。之所以进行此更改,原因是并不总是能够保证托管枚举的前向声明正常工作:编译器无法正确地标识枚举的基础类型。同时,C++ 标准不允许枚举声明。

  • /YX 编译器选项已被移除
    /YX 生成自动的预编译头支持。它是默认从开发环境使用的。如果从生成配置中移除 /YX,且不提供替换项,则可能使生成的速度更快。除了使用 /YX 时出现意外行为的可能性,最好是使用 /Yc(创建预编译头文件)/Yu(使用预编译头文件),这样可以更好地控制预编译头文件的使用方式。

  • /Oa/Ow 编译器选项已被移除
    /Ow/Oa 编译器选项已被移除,但将被忽略,并且没有提示。请使用 noaliasrestrict__declspec 修饰符来指定编译器命名别名的方式。

  • /Op 编译器选项已被移除
    /Op 编译器选项已被移除。应改用 /fp(指定浮点行为)

  • /ML/MLd 编译器选项已被移除
    Visual C++ 不再支持静态链接的单线程 CRT 库支持。 请改用 /MT/MTd。有关更多信息,请参见 C Run-Time Libraries

  • /G3/G4/G5/G6/G7/GB 编译器选项已被移除
    编译器现在使用混合模型,以试图创建对于所有结构而言均为最佳的输出文件。

  • /Gf 已被移除
    请改用 /GF(消除重复的字符串)/GF 将池中的字符串放入只读部分,该部分比 /Gf 在其中添加这些字符串的可写部分安全。

  • /clr/MT 不兼容
    在 C 运行时库中,不支持以静态方式链接到托管应用程序。必须动态地链接所有托管应用程序 (/MD)。有关使用 /clr 时的限制的更多信息,请参见 /clr 限制

  • 现在,/GS 默认情况下处于启用状态
    缓冲区溢出检查功能现在默认为打开状态。可以使用 /GS- 关闭缓冲区溢出检查功能。有关更多信息,请参见/GS(缓冲区安全检查)

  • 现在,/Zc:wchar_t 默认情况下处于启用状态
    这是标准 C++ 行为;wchar_t 变量将默认为内置类型,而不是短的无符号整数。当客户端代码与在未使用 /Zc:wchar_t (LNK2019) 的情况下编译的库链接时,此更改会破坏二进制兼容性。在这种情况下,请使用 /Zc:wchar_t- 恢复为原来的非标准行为。引入此更改是为了默认创建具有符合性的代码。

    有关更多信息,请参见/Zc:wchar_t(wchar_t 是本机类型)

  • 现在,/Zc:forScope 默认情况下处于启用状态
    这是标准 C++ 行为;现在,如果代码依赖于 for 循环中声明的变量在 for 循环范围结束后的使用,则此代码在编译时将失败。使用 /Zc:forScope- 恢复为原来的非标准行为。引入此更改是为了默认创建具有符合性的代码。

    有关更多信息,请参见/Zc:forScope(强制 for 循环范围中的一致性)

  • 强制对 Visual C++ 属性进行参数检查
    具有以下特点的代码现在将生成编译器错误 C2065编译器警告(等级 1)C4581:当类型不是字符串时,在将命名属性传递给属性构造函数时使用引号;当类型是字符串时,在将命名属性传递给属性构造函数时不使用引号。以前,所有编译器属性都被解析为字符串,并且编译器在需要时插入缺少的引号。属性支持通过增加参数检查验证而得到增强。此有助于防止由于向属性构造函数传递了错误的参数而产生意外的行为。

    对于此版本,在传递给采用隐式字符串作为参数的属性的任何参数中,您不能使用多字节字符串 (MBCS),即使该字符串括在引号中(这样做可能导致 .idl 文件损坏)。解决方法如下:

    #define ARG string_with_MBCS_chars
    [helpstring(ARG)]
    
  • 对于同一类型的多个声明,编译器现在需要相同的模板规范。
    如果有某个类型的前向声明以便能够创建该类型的友元,举例来说,类型的模板规范在该类型的所有声明上必须相同。否则,编译器将发出编译器错误 C2990

  • uuid 属性不能再针对托管类型
    在使用 C++ 托管扩展的用户定义属性上,允许有 uuid (C++ Attributes)属性,但现在将生成编译器错误 C3451。应改用 GuidAttribute

  • 将托管数组传递到自定义属性时所用语法的更改
    不再从聚合初始化列表中推导数组的类型。现在,编译器要求指定数组类型以及初始值设定项列表。旧语法现在将导致编译器错误 C3104。此更改是必需的,因为编译器无法始终正确地从聚合初始化列表中推导出数组类型。

  • 编译器将不会在声明中插入 int 作为默认类型
    声明中缺少该类型的代码将不再默认使用 int 类型,编译器将生成编译器警告 C4430编译器警告(等级 1)C4431。标准 C++ 不支持默认的 int,此更改将确保您获得真正需要的类型。

  • dynamic_cast 增强了 C++ 标准的一致性。
    C 运行时库现在执行 dynamic_cast 运行时检查,以确保所强制转换的表达式的编译时类型引用强制转换目标类型(用于向下强制转换)或派生程度最高的对象类型(用于交叉强制转换)的公共基类子对象。有关更多信息,请参见dynamic_cast 中的重大更改

  • rvalue 不能绑定到非常量引用。
    rvalue 不能绑定到非常量引用。在以前版本的 Visual C++ 中,直接初始化时有可能将 rvalue 绑定到非常量引用。此代码现在将生成编译器警告(等级 1)C4350

  • 值类型不再发出默认构造函数,这可能导致类型初始值设定项在不同的点运行
    在 Visual C++ 2005 之前,值类型中的静态构造函数(类型初始值设定项)会在创建值类型的实例时运行。为了确保静态构造函数运行,请访问静态数据成员或(仅限于 /clr:oldSyntax)定义一个实例构造函数。没有为值类型提供默认构造函数,因为公共语言运行库不保证它将始终调用默认构造函数。同时,不为值类型提供默认构造函数还可提高性能。

  • 在可验证的 (/clr:safe) 上下文中,已装箱的值类型现在是只读的。
    在编译可验证的程序集时,公共语言运行库不再允许修改已装箱的值类型。现在,编译器在检测到这一点时将提供编译器警告 C4972

    只有通过装箱的值对象更改基础值对象的值时,才会提供 C4792。如果更改值对象的副本(例如,更改已装箱的对象),该错误将不会发生

  • 默认情况下,本机类型在程序集外部是私有的
    现在,本机类型默认情况下在程序集外部将不可见。有关程序集外部类型可见性的更多信息,请参见Type Visibility。之所以进行此更改,主要是考虑到了使用其他不区分大小写的语言的开发人员在引用用 Visual C++ 编写的元数据时的需要。

  • /clr 现在接受 Visual C++ 新的 CLR 语法
    在 Visual C++ 2005 之前,/clr 编译 C++ 托管扩展语法。/clr 现在编译新的 CLR 语法,而 /clr:oldSyntax 编译 C++ 托管扩展语法。有关 /clr 的更多信息,请参见 /clr(公共语言运行库编译): 有关新语法的更多信息,请参见New C++ Language Features

  • /clr 不再编译 C 源代码文件
    在 Visual C++ 2005 之前,您可以用 /clr 编译 C 源代码文件,但是,这样做现在会导致命令行错误 D8045。若要解决此错误,请将文件扩展名更改为 .cpp 或 .cxx,或者用 /TP/Tp 进行编译。有关更多信息,请参见/Tc、/Tp、/TC、/TP(指定源文件类型)

  • MSIL 在测试相等性时发生更改
    有关更多信息,请参见如何:测试相等性

请参见

参考

Visual C++ 编译器中的重大更改