【问题标题】:Is new C++ backward compatible新的 C++ 是否向后兼容
【发布时间】:2010-12-01 15:38:53
【问题描述】:

我已经多年没有使用 C++ 编写代码了。我最近发现,在那些年里,它发生了巨大的变化。我不确定我是否喜欢这些更改,但这是另一个讨论。

我的硬盘上仍有一些 C++ 代码。如果我把它拿出来并尝试用一个不错的新 C++ 编译器编译它,比如最新版本的 g++,它会编译吗?没有警告(假设它之前编译时没有警告)?

我最近确实搞砸了一点 VC++ 2010,发现一些我认为可以正常工作的东西无法正常工作,并且当我尝试使用 NULL 时会根据上下文得到不同的消息。但是在该代码的一部分中,我使用 NULL 甚至没有警告。

【问题讨论】:

    标签: c++


    【解决方案1】:

    这取决于。通常较新的编译器更符合标准,因此在早期编译器上编译的许多结构在不修复的情况下现在无法编译。例如:

     for( int i = 0; ... );
     i++;
    

    在 Visual C++ 7 中编译,但不在 Visual C++ 9 中。

    【讨论】:

    • 所以 C++ 本身是向后兼容的,但实现它的编译器不是。
    • @Dialectus:因此,如果您使用非标准编译器扩展或利用编译器错误,那么新版本的编译器可能会破坏您的代码。这几乎是给定的,因为它是非标准行为!另一方面,如果您的代码是正确的 w.r.t.一开始的标准不应该有这样的问题。
    • @Billy ONeal:标准只是一个文本,还有一些编译器正在运行程序。很少有人根据标准编写 - 他们通常会在使用一个或多个感兴趣的编译器编译后解决。这就是为什么真正的代码几乎从不完全符合标准的原因。
    • @sharptooh。有道理。 OTOH,如果您的代码在 2 个或更多编译器上编译,它可能已经足够接近标准,因此在相同编译器的更高版本上几乎不会出现兼容性问题。
    • @Billy ONeal - 那个特定的 VS buglet 的问题在于它使有效的标准 C++(例如:在两个连续的 for 循环中声明 i)无法编译,所以你必须这样做非标准的东西(只需在第一个循环中声明它)。然后,一旦他们开始正确地遵循标准,他们的旧编译器强制执行的旧错误行为就会停止工作。
    【解决方案2】:

    一般来说,是的,它是向后兼容的。然而,魔鬼在细节中。您可能会发现约定发生变化的地方,或者特定库进入或停止使用的地方。

    【讨论】:

    • 当一个类开始需要移动构造函数以在使用新的 C++ 时正确运行时,我最近尝到了“细节中的魔鬼”的滋味。否则程序在使用新特性的 C++0x STL 代码中崩溃。
    【解决方案3】:

    NULL 是一个宏 - 更喜欢使用 0(或 nullptr in C++0x)。

    不确定您的代码到底有多旧,但 Visual C++ v6 受到限制,导致代码根本无法在较新的编译器上编译。 VS2005 及更高版本要好得多(在同时代的 C++ 标准更正确的意义上)。

    不过,我不希望编译旧代码需要大量工作。我已经从 VC6 -> VS2005 完成了一些非常重要的移植,而且大部分时间只需要几个小时,而不是几天。也许一旦文化冲击消退,这似乎就不会那么令人生畏了。真的,VC++ 10 很不错。

    【讨论】:

    • 其他人可以这样做:@undef NULL 然后#define NULL 1。愚蠢的例子,但这就是宏糟糕的原因之一。
    • @Javier 它可能被定义为(void*)0 而不仅仅是0,这意味着您不能将它用作通用空指针。或者,它可以是未定义的,也可以随意重新定义。
    • @Javier:C 是弱类型的,你可以将(void*)0 分配给任何指针。在 C++ 中,这需要显式转换。因此,如果宏是为 C 定义的,那么它可能会在 C++ 程序中中断。
    • 我是唯一一个喜欢 C 的 void*(可以转换为任何其他指针类型)和 NULL (void *)0 到 C++ 的人吗? int x = NULL; char *foo = 0; 在 C++ 中编译时没有错误,只是感觉不对...
    • @Steve:我记得这是一个关于这个主题的非常有趣的视频:channel9.msdn.com/Shows/Going+Deep/…>。我会总结一下,但已经有一段时间了,可能最好从马的嘴里得到它,因为它是。
    【解决方案4】:

    这取决于您要比较的对象。

    • Visual Studio 2010 部分实现了即将发布的 C++0x 草案(最新版本的 GCC 也实现了该草案的子集,预计明年将标准化)
    • C++98/C++03 是 C++ 的第一个标准化版本,并且仍然是官方版本(因为 C++0x 仍然只是一个草案)
    • 当然,还有语言标准化之前的方言

    C++0x 几乎向后兼容 C++03/98。可能有几个不起眼的极端案例发生了变化,但您不太可能遇到它们。 但是,当语言首次标准化时发生了很多变化,这意味着 C++98 并不完全(但几乎)与标准前的 C++ 兼容。

    但更有可能的是,您所关注的不是 C++ 向后兼容性的问题,而是编译器变得更加严格。他们在遵循标准方面变得更好,并且不再允许以前常见的许多非标准技巧。最有可能的是,您的旧代码从不是有效的 C++,但是因为编译器过去常常偏离标准,所以它可以工作。

    【讨论】:

      【解决方案5】:

      自 1998 年标准化以来,该语言本身并没有改变。习语发生了变化,编译器不仅改进了对标准的支持,而且对非标准代码更加严格,但任何符合标准的 C++ 代码在过去的今天仍然应该编译。依赖于非标准编译器功能或怪癖的代码可能无法正常工作,但编译器通常会提供命令行选项,以使它们接受过去同一编译器的先前版本所接受的非标准代码。

      NULL 是在<cstddef> 中定义的宏。其他标准标头可能包含该标头作为实现细节,因此通常可以使用 NULL 而不明确包含 <cstddef>,但依赖它始终是不可移植的。只要包含其标头就可以使用 NULL,尽管在惯用的 C++ 中首选使用 0。

      【讨论】:

      • 感谢您提供详细信息。尽管我工作过的所有地方似乎都更喜欢使用 NULL 而不是 0。
      • 自 98 年以来, 发生了一些变化。例如:auto 不再是存储类说明符;在C++0x中,相当于其他语言中的var,做自动类型推导。
      • grayfade,C++0x 还没有完成,也不是编译器的默认设置,所以我认为说语言已经发生了变化是不对的.不过,它改变。
      • AlastairG,实际上我也更喜欢 NULL——它让读者清楚地知道它是一个指针而不是一个整数。即将推出的 C++ 新版本(称为“C++0x”,尽管它实际上并没有在 200x 十年中完成)有一个新的“nullptr”关键字来代替 0。
      【解决方案6】:

      所有版本的 C++ 都应该向后兼容。

      虽然可能有一些不寻常的情况可能会出现问题,例如noexcept 在 C++0x 中的 destructos 上(尽管这还没有决定)。

      【讨论】:

        【解决方案7】:

        较新的 C++ 标准来澄清事情,然后决定哪种用法“更正确”或被接受,所以我希望得到警告。其他一些决定会改变可以做什么或不可以做什么,所以我也预计会出错。我遇到了同样的情况,我不得不调整代码才能编译。这不是什么大问题,但这是考虑到我对 C++ 的了解。一般来说,你应该不会遇到很大的问题。

        还有其他事情。例如,并非所有编译器都实现了 C++ 标准的全部规则,或者它们存在错误。当您使用编译器时,其中一些错误或缺少的功能可能会被您的编译器忽略,但可能会在同一编译器的未来版本中导致错误。

        【讨论】:

          【解决方案8】:

          这就是我们制定标准的主要原因。您不必担心兼容性,您只需告诉编译器将代码编译为 C++98(或 2003)即可。

          不幸的是,MSVC 在支持 C++ 标准方面非常糟糕。它正在慢慢变得更好(这就是旧代码无法编译的原因,因为它一开始就不应该编译)。

          【讨论】:

          • 我不能同意 MSVC 在一致性方面“非常糟糕”的描述。反正现在没有了。
          【解决方案9】:

          嗯,我知道当我们升级到 VS 2005 时,我们的很多旧 VS6 代码开始抛出大量警告(当然,有完整的警告,因为每个人都应该工作)。不过,大多数都是的,警告潜在的信息丢失,警告某些类型可能是 64 位而不是像旧代码预期的 32 位,等等。

          对于 NULL 的具体示例,那在当时甚至不是真正的标准 C。每个编译器都只有一个 #define 将其设置为 0。如今,通常认为 just use 0 更正确(和清晰)。

          【讨论】:

          • NULL 不标准?它可以追溯到 C89。它甚至比不少 C++ 程序员还要老。
          • 嗯,我可以追溯到更早的时候。我们过去实际上必须寻找 NULL 被定义为什么才能确定。一些编译器将其设置为奇怪的东西,偶尔表现不佳的源文件甚至会重新定义它。
          【解决方案10】:

          如果您使用过各种库(例如 Boost)的旧版本,预计会出现一些问题。

          【讨论】:

            【解决方案11】:

            有点随机,但关键字export 正在从标准中删除。因此,以前使用的符合标准的代码 export 现在将是非法的。当然,很少有编译器甚至开始实现该关键字。

            【讨论】:

              【解决方案12】:

              与 sharptooth 的回答非常相似,有些旧的 C 和 C++ 代码需要 /ZC:forScope-set (即不要强制符合 for 循环范围)。例如

              int myfunc(int x, int y, int z)
              {
                int j;
              
                for (int i=0; i <= 10; i++)
                {
                  if  (f(i) == 0)
                    break;
                }
                j = i;
                for (i=0; i <= 10; i++)
                {
                  if  (g(i) == 0)
                    break;
                }
                if (i > j)
                  return j;
                return i;
              }
              

              这种类型的事情在更旧的代码中很常见,其中字节需要花钱,变量重用很常见。

              【讨论】:

              • 嗯?这应该在没有任何开关的情况下编译——这是标准行为。 (特别是,你根本没有在 for 范围内放任何东西!)
              • 我的错。如果我编辑,移动声明
              猜你喜欢
              • 1970-01-01
              • 2021-07-22
              • 1970-01-01
              • 2011-08-27
              • 2019-11-14
              • 2020-01-16
              • 2020-11-15
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多