C++ 11中引入了哪些重大变化?

R. *_*des 226 c++ c++11

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

还有其他重大变化吗?

Joh*_*itb 176

FDIS有一个不兼容的部分,附录C.2"C++和ISO C++ 2003".

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

有一些与图书馆相关的不兼容性,我不完全知道它的含义,所以我留给其他人详细说明.

核心语言


#define u8 "abc"
const char *s = u8"def"; // Previously "abcdef", now "def"
Run Code Online (Sandbox Code Playgroud)
#define _x "there"
"hello"_x // now a user-defined-string-literal. Previously, expanded _x .
Run Code Online (Sandbox Code Playgroud)

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


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


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

(诚​​然,大多数人并不是兼容性问题).


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


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

int x[] = { 2.0 };
Run Code Online (Sandbox Code Playgroud)

当隐式定义不正确时,隐式声明的特殊成员函数被定义为已删除.

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

我的例子:

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

一些SFINAE已经使用了这么大的技巧,现在需要改变:)


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

我的例子:

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

int main() { try { A a; } catch(...) { } }
Run Code Online (Sandbox Code Playgroud)

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


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


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

在C++ 03中,>>永远是shift-operator标记.


允许具有内部链接的函数的依赖调用.

我的例子:

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

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

int main() { g(0); }
Run Code Online (Sandbox Code Playgroud)

在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); }
Run Code Online (Sandbox Code Playgroud)

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


图书馆变更

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


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


已编译的有效C++ 2003代码期望交换<algorithm>可能必须包含<utility>


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


限定有效的C++代码2003 override,final,carries_dependency,或noreturn为宏是C++ 0x中无效.


ars*_*enm 27

auto关键字的含义已更改.

  • @DrewDormann`int main(){auto int i = 0; 回归我; }`是完全有效的C++ 03,但是C++ 11中的语法错误.我可以让编译器在C++ 03模式下给出的唯一警告是关于兼容性的警告. (10认同)
  • 如果您一直在使用`auto`关键字,那么您的代码就会出现问题.为什么你会使用它? (9认同)

Dow*_*ter 23

突破变化?

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


Joh*_*itb 22

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


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

如果有效的C++ 03代码依赖于注入的类名在这些场景中始终是一种类型,则其行为可能会有所不同.从我的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();
}
Run Code Online (Sandbox Code Playgroud)

在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(); }
Run Code Online (Sandbox Code Playgroud)

这个调用的有效C++ 03代码A<int>::f在C++ 0x中无效,因为实例化时的名称查找将发现A<int>::f而不是B::f,导致与定义查找冲突.

此时,尚不清楚这是否是FDIS中的缺陷.委员会意识到这一点,并将评估情况.


一个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; }
Run Code Online (Sandbox Code Playgroud)

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


Lig*_*ica 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
}
Run Code Online (Sandbox Code Playgroud)

改变提案

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.[编辑:没有存储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 正确输出C++ 11:

    断言`x == -1'失败

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

    断言`x == -1'失败

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

    断言失败:x == 0

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

    断言失败:x == 0


Xeo*_*Xeo 13

如何引入显式转换运算符?旧版本仍将像以前一样"有效".

是的,从改变operator void*() const到改变explicit operator bool() const将是一个突破性的改变,但前提是它的使用方式本身就是错误的.符合规范的代码不会被破坏.

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

int a[] = { 1.0 }; // error
Run Code Online (Sandbox Code Playgroud)

编辑:只需记住,std::identity<T>将在C++ 0x中删除(参见注释).这是一种使类型依赖的便利结构.由于结构实际上没有做太多,这应该解决它:

template<class T>
struct identity{
  typedef T type;
};
Run Code Online (Sandbox Code Playgroud)


Jam*_*lis 8

容器库有许多变化,允许更高效的代码,但默默地破坏了一些极端情况的向后兼容性.

例如,考虑std::vector默认构造,C++ 0x和重大更改.


Ben*_*igt 7

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

(相关讨论的旧页面)

如果你读下评论,隐式移动返回也是一个突破性的变化.


Lig*_*ica 6

struct x {
   x(int) {}
};

void f(auto x = 3) { }

int main() {
   f();
}
Run Code Online (Sandbox Code Playgroud)

C++ 03:有效.

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

  • @Xeo:代码在C++ 03中有效.它是一个类型为`struct x`且没有名称的参数. (2认同)