【问题标题】:What breaking changes are introduced in C++11?C++11 中引入了哪些重大变化?
【发布时间】:2011-09-17 23:34:04
【问题描述】:

我知道 C++11 中至少有一项更改会导致一些旧代码停止编译:在标准库中引入 explicit operator bool(),替换旧的 operator void*() 实例。诚然,这会破坏的代码可能是原本不应该有效的代码,但它仍然是一个破坏性的变化:过去有效的程序不再有效。

还有其他重大变化吗?

【问题讨论】:

  • 删除 export 关键字的含义?我去拿外套。
  • 你知道,我不会将转换为布尔的变化称为“重大变化”......更像是“惩罚性变化”。
  • 当创建这样一个工会所需的所有文书工作都在等待盖章时,当然,为什么不呢?
  • @Xeo:mystream.good()bool(mystream)不一样?如果未设置标志,good() 为真。如果只设置了eofbitbool(mystream) 仍然为 false。 !mystream.fail() 是正确的等价物。
  • 版主说明:“请保持 cmets 的主题与手头的问题或答案。在讨论问题或答案时,讨论应该是这样的,手头的问题或答案。辩论,一般来说对 Stack Overflow 没有建设性。对抗肯定不是。"

标签: c++ c++11


【解决方案1】:

FDIS 在附录 C.2“C++ 和 ISO C++ 2003”中有一个关于不兼容性的部分。

总结,在这里解释 FDIS,使其(更好)适合作为 SO 答案。我添加了一些我自己的例子来说明差异。

有一些与库相关的不兼容问题我并不完全了解其含义,因此我将其留给其他人详细说明。

核心语言


#define u8 "abc"
const char *s = u8"def"; // Previously "abcdef", now "def"

#define _x "there"
"hello"_x // now a user-defined-string-literal. Previously, expanded _x .

新关键字:alignas、alignof、char16_t、char32_t、constexpr、decltype、noexcept、nullptr、static_assert 和 thread_local


某些大于 long 可以表示的整数文字可以从无符号整数类型更改为有符号 long long。


使用整数除法的有效 C++ 2003 代码将结果向 0 或负无穷大四舍五入,而 C++0x 始终将结果向 0 四舍五入。

(诚然,对于大多数人来说,这并不是真正的兼容性问题)。


使用关键字auto 作为存储类说明符的有效 C++ 2003 代码在 C++0x 中可能无效。


缩小转换范围会导致与 C++03 不兼容。例如,以下代码在 C++ 2003 中有效,但在本国际标准中无效,因为 double 到 int 是一种缩小转换:

int x[] = { 2.0 };

当隐式定义格式错误时,隐式声明的特殊成员函数被定义为删除。

在不需要定义的上下文中使用这些特殊成员函数之一的有效 C++ 2003 程序(例如,在可能不会被评估的表达式中)变得不正确。

我的例子:

struct A { private: A(); };
struct B : A { };
int main() { sizeof B(); /* valid in C++03, invalid in C++0x */ }

一些 SFINAE 已经使用了这种 sizeof 技巧,现在需要更改 :)


用户声明的析构函数有一个隐式的异常规范。

我的例子:

struct A {
  ~A() { throw "foo"; }
};

int main() { try { A a; } catch(...) { } }

此代码在 C++0x 中调用 terminate,但在 C++03 中不调用。因为 C++0x 中A::~A 的隐式异常规范是noexcept(true)


包含export 的有效C++ 2003 声明在C++0x 中格式不正确。


现在可以将包含> 后跟另一个> 的有效C++ 2003 表达式视为关闭两个模板。

在 C++03 中,>> 始终是移位运算符标记。


允许对具有内部链接的函数进行依赖调用。

我的例子:

static void f(int) { }
void f(long) { }

template<typename T>
void g(T t) { f(t); }

int main() { g(0); }

在 C++03 中,此调用 f(long),但在 C++0x 中,此调用 f(int)。需要注意的是,在 C++03 和 C++0x 中,以下调用 f(B)(实例化上下文仍然只考虑外部链接声明)。

struct B { };
struct A : B { };

template<typename T>
void g(T t) { f(t); }

static void f(A) { }
void f(B) { }

int main() { A a; g(a); }

没有采用更好匹配的f(A),因为它没有外部链接。


库更改

使用添加到 C++ 标准的任何标识符的有效 C++ 2003 代码 C++0x 库可能无法编译或在本国际标准中产生不同的结果。


#includes 标头名称为新 C++0x 标准库标头的有效 C++ 2003 代码在本国际标准中可能无效。


已编译的有效 C++ 2003 代码预期交换位于 &lt;algorithm&gt; 中可能必须改为包含 &lt;utility&gt;


全局命名空间 posix 现在保留用于标准化。


overridefinalcarries_dependencynoreturn 定义为宏的有效 C++ 2003 代码在 C++0x 中无效。

【讨论】:

  • “允许对具有内部链接的函数进行依赖调用。”您能否详细说明您的两个示例之间的区别?我显然错过了一些东西。
  • @Dennis 更改是由 open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#561 引入的。尽管他们没有评论这一事实,但“实例化上下文”仍然仅包含“在同一翻译单元中的模板专业化实例化点之前声明的具有外部链接的声明集”。所以他们所做的更改只会影响定义上下文中的查找。
  • 在我给出的第一个示例中,内部链接函数是可见的,并且可以在模板的定义上下文中找到。在我的第二个示例中,内部链接函数需要成为要找到的实例化上下文的一部分。但既然不是,那就找不到了。
  • 顺便说一下,我认为模板定义上下文可以安全地找到具有内部链接的函数的唯一情况是,函数模板特化仅在一个 TU 中显式实例化(其中定义了模板),并且所有其他 TU 都依赖于该显式实例化。在所有其他情况下(其他 TU 将自己实例化特化),您将违反 ODR,因为模板定义每次都使用不同的(内部链接)函数。
  • 所以我不确定他们为什么保留对实例化上下文的限制 - 只有一个(显式)实例化,并且实例化将使用在实例化的实例化上下文中找到的内部链接函数恩。就像定义上下文一样。顺便说一句,我认为如果我们仍然拥有export,那么我认为其他 TU 不需要依赖显式实例化,而是可以自己实例化模板。 那么内部链接函数在实例化上下文中是否可见会有所不同。
【解决方案2】:

auto关键字的含义改变了。

【讨论】:

  • 如果您一直在使用auto 关键字,那么您的代码有问题。你到底为什么要使用它?
  • 这不是一个重大变化。每个有效的 C++03 使用 auto 在 C++11 中仍然有效。
  • @DrewDormann int main() { auto int i = 0; return i; } 是完全有效的 C++03,但在 C++11 中存在语法错误。我可以让编译器在 C++03 模式下给出的唯一警告是关于兼容性的警告。
【解决方案3】:

重大变化?

好吧,一方面,如果您使用decltypeconstexprnullptr 等作为标识符,那么您可能会遇到麻烦...

【讨论】:

    【解决方案4】:

    不兼容性部分未涵盖的一些核心不兼容性:


    C++0x 将注入的类名视为模板,如果该名称作为参数传递给模板模板参数,并且如果它被传递给模板类型参数,则视为类型。

    如果有效的 C++03 代码依赖注入的类名在这些场景中始终是一种类型,则其行为可能会有所不同。示例代码taken from my clang PR

    template<template<typename> class X>
    struct M { };
    
    template<template<typename> class X>
    void g(int = 0); // #1
    
    template<typename T>
    void g(long = 0); // #2
    
    template<typename T>
    struct A {
      void f() {
        g<A>(); /* is ambiguous in C++0x */
        g<A>(1); /* should choose #1 in C++0x */
      }
    };
    
    void h() {
      A<int> a;
      a.f();
    }
    

    在 C++03 中,代码两次调用第二个 g


    C++0x 使一些在 C++03 中依赖的名称现在变为非依赖的。并且需要在实例化时重复引用当前类模板成员的非依赖限定名称的名称查找,并且需要验证这些名称的查找方式与在模板定义上下文中完成的方式相同。

    由于此更改,依赖于支配规则的有效 C++03 代码现在可能不再编译。

    例子:

    struct B { void f(); };
    
    template<typename T>
    struct A : virtual B { void f(); };
    
    template<typename T>
    struct C : virtual B, A<T> {
      void g() { this->f(); }
    };
    
    int main() { C<int> c; c.g(); }
    

    调用A&lt;int&gt;::f 的有效C++03 代码在C++0x 中无效,因为实例化时的名称查找将找到A&lt;int&gt;::f 而不是B::f,从而导致与定义时查找发生冲突.

    目前尚不清楚这是否是 FDIS 的缺陷。委员会已意识到这一点,并将评估情况。


    一个 using 声明,其中最后一部分与限定名称中限定符的最后一部分中的标识符相同,表示基类,该 using 声明现在命名构造函数,而不是具有该名称的成员。

    例子:

    struct A { protected: int B; };
    typedef A B;
    
    struct C : B {
      // inheriting constructor, instead of bringing A::B into scope
      using B::B;
    };
    
    int main() { C c; c.B = 0; }
    

    上面的示例代码在 C++03 中格式正确,但在 C++0x 中格式错误,因为在 main 中仍然无法访问 A::B

    【讨论】:

      【解决方案5】:

      流提取失败的处理方式不同。

      示例

      #include <sstream>
      #include <cassert>
      
      int main()
      {
         std::stringstream ss;
         ss << '!';
         
         int x = -1;
         
         assert(!(ss >> x)); // C++03 and C++11
         assert(x == -1);    // C++03
         assert(x == 0);     // C++11
      }
      

      更改提案

      http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3246.html#23

      标准参考

      [C++03: 22.2.2.1.2/11]:第二阶段处理的结果可以是其中之一

      • 在阶段 2 中累积了一个字符序列,该字符序列被转换(根据scanf 的规则)为val 类型的值。该值存储在val 中,ios_base::goodbit 存储在err 中。
      • 在第 2 阶段累积的字符序列会导致 scanf 报告输入失败。 ios_base::failbit 分配给 err[ed:val 中没有存储任何内容。]

      [C++11: 22.4.2.1.2/3]: [..] 要存储的数值可以是以下之一:

      • 零,如果转换函数无法转换整个字段ios_base::failbit 分配给 err
      • 可表示的最正值,如果该字段表示的值太大而无法在val 中表示。 ios_base::failbit 分配给 err
      • 如果字段表示的值太大而无法在val 中表示,则为无符号整数类型可表示的最大负值或零。 ios_base::failbit 分配给 err
      • 转换后的值,否则。

      结果数值存储在val中。

      实现

      • GCC 4.8 correctly outputs for C++11:

        断言 `x == -1' 失败

      • GCC 4.5-4.8 all output for C++03 以下,这似乎是一个错误:

        断言 `x == -1' 失败

      • Visual C++ 2008 Express 正确输出 C++03:

        断言失败:x == 0

      • Visual C++ 2012 Express 错误地输出 C++11,这似乎是一个实施状态问题:

        断言失败:x == 0

      【讨论】:

        【解决方案6】:

        显式转换运算符的引入有何重大改变?旧版本仍然和以前一样“有效”。

        是的,从operator void*() constexplicit operator bool() const 的更改将是一个重大更改,但前提是它的使用方式本身是错误的。符合标准的代码不会被破坏。

        现在,另一个重大变化是the banning of narrowing conversions during aggregate initialization

        int a[] = { 1.0 }; // error
        

        编辑:请记住,std::identity&lt;T&gt; will be removed in C++0x(见注释)。这是一种使类型依赖的便利结构。由于结构确实没有做太多,这应该可以解决它:

        template<class T>
        struct identity{
          typedef T type;
        };
        

        【讨论】:

        • 如果标准库对象添加了显式转换,那么现有的隐式转换可能会停止工作。但我无法想象转换无效并做一些有用的事情的场景。
        • 引入是一项重大更改,因为它将替换现有的operator void*
        • @Dennis:啊啊,我现在明白@Martinho 的意思了。但只有当人们以非预期方式使用它时,它才会是一个突破性的变化。
        • "但只有当它被以一种内外错误的方式使用时" -- bool ok = cin &gt;&gt; a; cout &lt;&lt; "done reading" &lt;&lt; endl; if (ok) { ... } 在 C++03 中并没有什么真正的问题,但它已经变成了一个错误在 C++11 中。 (注意:GCC 4.9 在这里仍然有operator void*() const,这就是它接受 C++11 模式的代码的原因。)
        • std::identity&lt;T&gt; 在 C++11 中没有被删除,因为它不是 C++03 的一部分。它确实存在于 C++11 草案中,并在标准化之前从草案中删除。
        【解决方案7】:

        对容器库进行了许多更改,这些更改允许更高效的代码,但会在一些极端情况下默默地破坏向后兼容性。

        例如,考虑std::vector, default construction, C++0x, and breaking changes

        【讨论】:

          【解决方案8】:

          implicit move breaking backward compatibility的讨论很多

          (an older page with relevant discussion)

          如果你仔细阅读 cmets,隐式移动返回也是一个重大变化。

          【讨论】:

          • 这些讨论的结果是它几乎在所有情况下都被删除了。剩下的有什么问题吗?
          • @Dennis:是的。你的问题已经在this followup pagethis followup page 上被问到、回答和辩论过
          • 啊,移动页面没有显示 cmets。无论哪种方式,这都是更有用的链接......标准化过程的历史怪异并不那么相关(除非您使用的是 MSVC,我相信它使用的是初稿)。
          • @Dennis:我认为你是对的。在我的答案中移动了一些链接。
          • 遗憾的是,cpp-next.com 不再存在。供将来参考,这些是 web.archive.org 保存的页面:implicit move breaking backward compatibilityan older page with relevant discussion
          【解决方案9】:
          struct x {
             x(int) {}
          };
          
          void f(auto x = 3) { }
          
          int main() {
             f();
          }
          

          C++03:有效。

          C++0x: error: parameter declared 'auto'

          【讨论】:

          • @Xeo:代码在 C++03 中有效。这是一个类型为struct x 且没有名称的参数。
          • 我希望能抓到一个人。我只希望@Xeo 没有这么快删除他的评论,因为我没能看到它!
          • @Xeo:如果不深入研究语法,我肯定 auto 在这里不是一个有效的关键字。如果是这样,它可能会按您的预期工作,但这可能很难正确定义。
          • 假设你抓住了我。它实际上忽略了结构。 :)
          • @Tomalek:Xeo 正确地指出 C++03 没有隐式 int。
          猜你喜欢
          • 2018-03-20
          • 1970-01-01
          • 2011-09-08
          • 1970-01-01
          • 2012-02-09
          • 1970-01-01
          • 2010-10-26
          相关资源
          最近更新 更多