C ++ 11中引入了哪些重大更改?


227

我知道C ++ 11中的至少一项更改会导致一些旧代码停止编译:explicit operator bool()在标准库中引入,替换了的旧实例operator void*()。当然,将要中断的代码可能最初应该是无效的代码,但是仍然是一个重大更改:曾经有效的程序不再有效。

还有其他重大变化吗?


1
删除export关键字的含义?我给我外套。
史蒂夫·杰索普

7
您知道,我不会将转换为布尔型转换称为“重大更改” ...更像是“惩罚性更改”。
Xeo

4
当创建这样一个联合所需的所有文书工作都只是在等待被橡皮图章印记时,为什么呢?
2011年

3
@Xeo: mystream.good()和不一样bool(mystream)吗?good()如果未设置标志,则为true。bool(mystream)如果仅eofbit设置,仍为false 。!mystream.fail()将是正确的等效项。
R. Martinho Fernandes

2
主持人注意:“ 请保留有关主题的评论,并附有问题或答案。在讨论问题或答案时,讨论应仅是眼前的问题或答案。辩论通常对堆栈溢出没有建设性作用。当然不是对抗。
蒂姆·波斯特

Answers:


178

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 */ }

这样的sizeof技巧已被一些SFINAE使用,现在需要更改:)


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

我的例子:

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

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

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


有效的C ++ 2003声明export在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 ++ 0x的C ++标准库中的任何标识符的有效C ++ 2003代码可能无法在本国际标准中编译或产生不同的结果。


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


预期已插入交换的有效C ++ 2003代码<algorithm>可能必须包含<utility>


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


有效的C ++代码2003定义overridefinalcarries_dependency,或noreturn为宏是C ++ 0x中无效。


“允许具有内部链接的函数的相关调用。” 您能否详细说明两个示例之间的区别?我显然缺少了一些东西。
2011年

@Dennis,该更改是由open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#561引入的。尽管他们没有对此事实发表评论,但“实例上下文”仍然仅由“具有在同一翻译单元中的模板专业化实例化点之前声明的具有外部链接的声明集”组成。因此,他们所做的更改只会影响定义上下文中的查找。
Johannes Schaub-litb 2011年

在我给出的第一个示例中,内部链接函数在模板的定义上下文中可见并找到。在我的第二个示例中,内部链接功能将需要成为实例化上下文的一部分。但是由于它不是,所以找不到它。
Johannes Schaub-litb 2011年

顺便说一句,我认为对于模板定义上下文来说,找到具有内部链接的函数的唯一安全情况是,仅在一个TU(定义了模板)中实例化函数模板特化实例化模板,而所有其他TU都依赖明确的实例化。在所有其他情况下(其他TU将自己实例化专门化),通过让模板定义每次使用不同的(内部链接)功能,将违反ODR。
Johannes Schaub-litb 2011年

因此,我不确定他们为什么对实例化上下文保持限制-只会有一个(显式)实例化,而实例化将使用实例化TU实例化上下文中的内部链接函数。就像定义上下文一样。顺便说一句,我认为如果我们仍然有export,那么我认为其他TU不需要依赖显式实例化,而是可以自己实例化模板。然后,内部链接功能在实例化上下文中是否可见将有所不同。
Johannes Schaub-litb 2011年

28

auto关键字的含义已更改。


9
如果您一直在使用auto关键字,则您的代码出了点问题。为什么在地球上会使用它?
Elazar Leibovich

这不是一个重大变化。每次对C ++ 03的auto有效使用在C ++ 11 中仍然有效。
德鲁·多曼

11
@DrewDormann int main() { auto int i = 0; return i; }是完全有效的C ++ 03,但是C ++ 11中存在语法错误。我可以让编译器在C ++ 03模式下给出的唯一警告是关于兼容性的警告。

24

零钱吗?

那么,对于一件事,如果你使用decltypeconstexprnullptr等作为标识符,那么你可能会遇到麻烦?


21

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


如果名称作为模板模板参数的参数传递,则C ++ 0x将注入的类名称视为模板;如果将名称传递给模板类型参数,则C ++ 0x将其视为类型。

如果有效的C ++ 03代码在某些情况下依赖于注入的类名始终是一种类型,则其行为可能会有所不同。从我的c 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(); }

此调用的有效C ++ 03代码A<int>::f在C ++ 0x中无效,因为实例化时的名称查找将A<int>::fB::f,从而导致与at定义查找冲突。

在这一点上,尚不清楚这是否是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中格式不正确,因为A::B仍然无法访问main


14

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

#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阶段处理的结果可以是以下之一

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

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

  • 零,如果转换函数无法转换整个字段ios_base::failbit被分配给err
  • 最正的可表示值,如果该字段表示一个太大的正值而无法用 valios_base::failbit被分配给err
  • 如果该字段表示负的值太大而无法在 valios_base::failbit被分配给err
  • 转换后的值,否则。

结果数值存储在 val

实作

  • GCC 4.8 正确输出C ++ 11

    断言x == -1失败

  • GCC 4.5-4.8 C ++ 03的所有输出的如下所示,这似乎是一个错误:

    断言x == -1失败

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

    断言失败:x == 0

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

    断言失败:x == 0


13

引入显式转换运算符如何带来重大变化?旧版本仍然像以前一样“有效”。

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

现在,另一个重大变化是在聚合初始化期间禁止缩小转换范围

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

编辑:只是记住者,std::identity<T>将在C ++ 0x中删除(请参阅注释)。使类型依赖是一种方便的结构。由于该结构实际​​上并没有做什么用,因此应该修复它:

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

如果标准库对象添加了显式转换,则现有的隐式转换可能会停止工作。但是我无法想象这种转换将无效并且做一些有用的事情的情况。
丹尼斯·齐克福斯

简介是一项重大更改,因为它将取代 现有的operator void*
R. Martinho Fernandes

@丹尼斯:啊,我现在明白了@马丁尼的意思。但是,如果人们非预期地使用它,那将是一个重大的变化。
Xeo

“但仅当其使用方式本身是错误的时才使用” – bool ok = cin >> a; cout << "done reading" << endl; if (ok) { ... }C ++ 03 中并没有什么真正的错误,但在C ++ 11中它已成为错误。(注意:GCC 4.9仍然在operator void*() const此处,这就是为什么它确实接受C ++ 11模式下的代码的原因。)

std::identity<T>在C ++ 11中没有删除,因为它不是C ++ 03的一部分。它确实存在于C ++ 11的草案中,并且在标准化之前已从草案中删除。
Howard Hinnant


7

关于隐式移动破坏向后兼容性的讨论很多

带有相关讨论的旧页面

如果您仔细阅读注释,隐式的移动返回也是一项重大更改。


这些讨论的结果是,几乎在所有情况下都将其删除。剩下的东西有什么问题吗?
丹尼斯·齐克福斯

@丹尼斯:是的。在此后续页面
Ben Voigt

啊,移动页面没有显示评论。无论哪种方式,这都是更有用的链接...标准化过程的历史古怪并不是那么重要(除非您使用的是MSVC,我相信它使用的是第一稿)。
2011年

@丹尼斯:我认为你是对的。在我的答案中移动了一些链接。
Ben Voigt

可悲的是,cpp-next.com不再存在。这些都是web.archive.org保存的页面,以供将来参考:隐式移动破坏了向后兼容性,并且页面较旧进行了相关讨论
Max Truxa 2015年

6
struct x {
   x(int) {}
};

void f(auto x = 3) { }

int main() {
   f();
}

C ++ 03:有效。

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


2
@Xeo:该代码在C ++ 03中有效。这是一个带有类型struct x且没有名称的参数。
Ben Voigt

我希望能找到一个人。我只希望@Xeo能够这么快地删除他的评论,因为我没看懂!
Lightness Races in Orbit

@Xeo:在不深入语法的情况下,我确定auto并不是那里的有效关键字。如果是这样,它可能会按您期望的那样工作,但这可能很难正确定义。
丹尼斯·齐克福斯

假设您抓住了我。它实际上忽略了该结构。:)
Xeo

@Tomalek:Xeo正确地指出C ++ 03没有隐式int。
Ben Voigt

-4

语言特征

  1. 使用{}进行统一和常规初始化
  2. 汽车
  3. 防止变窄
  4. constexpr
  5. 基于范围的循环
  6. nullptr
  7. 枚举类
  8. static_assert
  9. std :: initializer_list
  10. 右值引用(移动语义)
  11. >>
  12. Lambdas
  13. 可变参数模板
  14. 类型和模板别名
  15. Unicode字符
  16. long long整数类型
  17. alignas和alignof
  18. 十进制
  19. 原始字符串文字
  20. 广义POD
  21. 广义工会
  22. 本地类作为模板参数
  23. 后缀返回类型语法
  24. [[carries_dependency]]和[[noreturn]]
  25. 没有指定符
  26. noexcept运算符。
  27. C99功能:
    • 扩展积分类型
    • 窄/宽字符串的串联
    • _ _ STDC_HOSTED _ _
    • _Pragma(X)
    • vararg宏和空宏参数
  28. _ _功能_ _
  29. 内联名称空间
  30. 委托构造函数
  31. 课堂成员初始化器
  32. 默认并删除
  33. 显式转换运算符
  34. 用户定义的文字
  35. 外部模板
  36. 函数模板的默认模板参数
  37. 继承构造函数
  38. 覆盖和最终
  39. 更简单,更通用的SFINAE规则
  40. 记忆模型
  41. thread_local

标准库组件

  1. 容器的initializer_list
  2. 移动容器的语义
  3. 转发列表
  4. 哈希容器
    • unordered_map
    • unordered_multimap
    • 无序集
    • unordered_multiset
  5. 资源管理指标
    • unique_ptr
    • shared_ptr
    • weak_ptr
  6. 并发支持
    • 线
    • 互斥体
    • 条件变量
  7. 更高级别的并发支持
    • packaged_thread
    • 未来
    • 诺言
    • 异步的
  8. 元组
  9. 正则表达式
  10. 随机数
    • uniform_int_distribution
    • 正态分布
    • random_engine
    • 等等
  11. 整数类型名称,例如int16_t,uint32_t和int_fast64_t
  12. 数组
  13. 复制和重新抛出异常
  14. 系统错误
  15. 容器的emplace()操作
  16. constexpr函数
  17. 系统地使用noexcept函数
  18. 功能和绑定
  19. 字符串到数值的转换
  20. 范围分配器
  21. 类型特征
  22. 时间工具:持续时间和time_point
  23. quick_exit
  24. 更多算法,例如move(),copy_if()和is_sorted()
  25. 垃圾回收ABI
  26. 原子

不推荐使用的功能

  1. 复制构造函数的生成以及具有析构函数的类的复制分配。
  2. 将字符串文字分配给char *。
  3. C ++ 98异常规范
    • unexcepted_handler
    • set_unexpected
    • get_unexpected
    • 意外
  4. 功能对象和相关功能
  5. auto_ptr
  6. 寄存器
  7. ++布尔
  8. 出口
  9. C型演员表

3
这不能回答问题。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.