我知道c++ 11中至少有一个变化会导致一些旧代码停止编译:在标准库中引入显式操作符bool(),取代操作符void*()的旧实例。当然,这将破坏的代码可能是本来就不应该有效的代码,但它仍然是一个破坏性的更改:过去有效的程序不再有效。
还有其他重大变化吗?
我知道c++ 11中至少有一个变化会导致一些旧代码停止编译:在标准库中引入显式操作符bool(),取代操作符void*()的旧实例。当然,这将破坏的代码可能是本来就不应该有效的代码,但它仍然是一个破坏性的更改:过去有效的程序不再有效。
还有其他重大变化吗?
显式转换运算符的引入如何成为突破性的变化?旧版本仍然和以前一样“有效”。
是的,从操作符void*() const到显式操作符bool() const的变化将是一个破坏性的变化,但前提是它的使用方式内外都是错误的。符合规范的代码不会被打破。
现在,另一个突破性的变化是在聚合初始化期间禁止收缩转换:
int a[] = { 1.0 }; // error
编辑:请记住,std::identity<T>将在c++ 0x中被删除(参见注释)。它是一种方便的结构体,可以使类型相互依赖。由于结构体实际上并没有做太多的事情,这应该可以修复它:
template<class T>
struct identity{
typedef T type;
};
struct x {
x(int) {}
};
void f(auto x = 3) { }
int main() {
f();
}
C++03:有效。
c++ 0x:错误:参数声明为auto
对容器库进行了大量更改,可以实现更高效的代码,但在一些极端情况下会默默地破坏向后兼容性。
例如,考虑std::vector、默认结构、c++ 0x和破坏性更改。
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使用了这种尺寸的技巧,现在需要改变:)
用户声明的析构函数具有隐式异常规范。
我举的例子:
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中,>>将始终是shift运算符令牌。
允许具有内部链接的函数的依赖调用。
我举的例子:
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)(实例化上下文仍然只考虑extern链接声明)。
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的库可能无法编译或在本标准中产生不同的结果。
包含新c++ 0x标准库标头名称的有效c++ 2003代码在本标准中可能无效。
已编译的期望交换在<算法>中的有效c++ 2003代码可能必须包含<实用程序>
全局名称空间posix现在被保留用于标准化。
将override、final、carries_dependency或noreturn定义为宏的有效c++ 2003代码在c++ 0x中无效。
一些核心不兼容性在不兼容性部分中没有涉及:
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();
}
在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<int>::f的有效c++ 03代码在c++ 0x中无效,因为实例化时的名称查找将找到A<int>::f而不是B::f,导致与at-definition查找冲突。
在这一点上,还不清楚这是否是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中仍然不可访问。
流提取失败的处理方式不同。
例子
#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]: The result of stage 2 processing can be one of A sequence of chars has been accumulated in stage 2 that is converted (according to the rules of scanf) to a value of the type of val. This value is stored in val and ios_base::goodbit is stored in err. The sequence of chars accumulated in stage 2 would have caused scanf to report an input failure. ios_base::failbit is assigned to err. [ed: Nothing is stored in val.] [C++11: 22.4.2.1.2/3]: [..] The numeric value to be stored can be one of: zero, if the conversion function fails to convert the entire field. ios_base::failbit is assigned to err. the most positive representable value, if the field represents a value too large positive to be represented in val. ios_base::failbit is assigned to err. the most negative representable value or zero for an unsigned integer type, if the field represents a value too large negative to be represented in val. ios_base::failbit is assigned to err. the converted value, otherwise. The resultant numeric value is stored in 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