答案是肯定的。好处在于:
不利方面,在标准的附录C中列出了几个例子。尽管负面的例子比正面的多得多,但每一个负面例子发生的可能性都要小得多。
字符串字面值
#define u8 "abc"
const char* s = u8"def"; // Previously "abcdef", now "def"
和
#define _x "there"
"hello "_x // Previously "hello there", now a user defined string literal
0的类型转换
在C++11中,只有字面值可以作为整数空指针常量:
void f(void *); // #1
void f(...); // #2
template<int N> void g() {
f(0*N); // Calls #2; used to call #1
}
整数除法和取余后的结果进行四舍五入
C++03编译器允许向0或负无穷方向进行四舍五入。在C++11中,强制必须向0进行四舍五入。
int i = (-1) / 2; // Might have been -1 in C++03, is now ensured to be 0
嵌套模板结束大括号之间的空格 >> vs > >
在特化或实例化的内部,>>
可能会被解释为 C++03 中的右移。然而,这更有可能破坏现有代码:(来自http://gustedt.wordpress.com/2013/12/15/a-disimprovement-observed-from-the-outside-right-angle-brackets/)
template< unsigned len > unsigned int fun(unsigned int x);
typedef unsigned int (*fun_t)(unsigned int);
template< fun_t f > unsigned int fon(unsigned int x);
void total(void) {
// fon<fun<9> >(1) >> 2 in both standards
unsigned int A = fon< fun< 9 > >(1) >>(2);
// fon<fun<4> >(2) in C++03
// Compile time error in C++11
unsigned int B = fon< fun< 9 >>(1) > >(2);
}
操作符new
现在可能会抛出除std::bad_alloc
之外的其他异常
struct foo { void *operator new(size_t x){ throw std::exception(); } }
try {
foo *f = new foo();
} catch (std::bad_alloc &) {
// c++03 code
} catch (std::exception &) {
// c++11 code
}
用户声明的析构函数具有隐含的异常规范 C++11中引入的哪些破坏性变化?举例说明。
struct A {
~A() { throw "foo"; } // Calls std::terminate in C++11
};
//...
try {
A a;
} catch(...) {
// C++03 will catch the exception
}
size()
方法现在要求容器的时间复杂度为O(1)
std::list<double> list;
// ...
size_t s = list.size(); // Might be an O(n) operation in C++03
std::ios_base::failure
不再直接派生自std::exception
虽然直接基类是新的,但std::runtime_error
不是。因此:
try {
std::cin >> variable; // exceptions enabled, and error here
} catch(std::runtime_error &) {
std::cerr << "C++11\n";
} catch(std::ios_base::failure &) {
std::cerr << "Pre-C++11\n";
}
noexecpt(true)
特性,因此在析构函数中抛出异常将会调用 std::terminate
。但我希望编写这种代码的人会对此感到满意! - typ1232catch (std::exception &)
仍然可以捕获 std::ios_base::failure。 - user2665887operator new
的内容是准确的(它现在可以抛出 std::bad_array_new_length
异常),但你展示的例子并没有显示出来。据我所知,你展示的代码在 C++03 和 C++11 中是相同的。 - Mooing Duck我指向这篇文章和其后续文章,其中有一个很好的例子显示如何在C++03和C++11中仍能编译的情况下,>>
的含义是如何从C++03转变为C++11的。
bool const one = true;
int const two = 2;
int const three = 3;
template<int> struct fun {
typedef int two;
};
template<class T> struct fon {
static int const three = ::three;
static bool const one = ::one;
};
int main(void) {
fon< fun< 1 >>::three >::two >::one; // valid for both
}
关键部分是在main
中的一行表达式。
1 >> ::three = 0
=> fon< fun< 0 >::two >::one;
fun< 0 >::two = int
=> fon< int >::one
fon< int >::one = true
=> true
fun< 1 > is a type argument to fon
fon< fun<1> >::three = 3
=> 3 > ::two > ::one
::two is 2 and ::one is 1
=> 3 > 2 > 1
=> (3 > 2) > 1
=> true > 1
=> 1 > 1
=> false
恭喜,相同的表达式得出了两个不同的结果。虽然当我测试时,C++03的那个会在Clang中产生一个警告。::two
不需要使用typename
这一点很奇怪。 - zahirint main()
{
int count = 0 ;
int arrInt[2] = { count++, count++ } ;
return 0 ;
}
在C++03和C++11中,这是良好定义的,但是在C++03中,评估顺序是未指定的,而在C++11中,它们按照出现的顺序进行评估。因此,如果我们使用clang
以C++03模式编译,它会提供以下警告(请查看实时演示):
warning: multiple unsequenced modifications to 'count' [-Wunsequenced]
int arrInt[2] = { count++, count++ } ;
^ ~~
但是在C++11中没有提供警告(请查看实况)。
2. C++11的新排序规则使得 i = ++ i + 1; 的行为被定义。
C++03之后采用的新排序规则意味着:
int i = 0 ;
i = ++ i + 1;
C++11中已经不再将其视为未定义行为,这在缺陷报告637.顺序规则和示例的不一致中有所涵盖。
3. C++11引入的新顺序规则也使++++i;的行为定义明确了。
C++03之后采用的新顺序规则意味着:
int i = 0 ;
++++i ;
C++11中,is no longer undefined behavior
。
4. 稍微更合理的有符号左移
C++11后续的草案中包括 N3485
,我在下面提供链接fixed the undefined behavior of shifting a 1 bit into or past the sign bit。这也涵盖在defect report 1457中。Howard Hinnant在Is left-shifting (<<) a negative integer undefined behavior in C++11?线程中评论了这个变化的重要性。
5. constexpr函数可以在C++11中被视为编译时常量表达式
C++11引入了constexpr函数,它们:
关键字声明可以在编译时计算函数或变量的值。这样的变量和函数可以在只允许编译时常量表达式的地方使用。虽然C++03没有constexpr特性,但是我们不必显式地使用constexpr关键字,因为标准库提供了许多在C++11中作为constexpr的函数。例如std::numeric_limits::min。这可能会导致不同的行为,例如:
#include <limits>
int main()
{
int x[std::numeric_limits<unsigned int>::min()+2] ;
}
在C++03中使用clang
将导致x
成为变长数组,这是一种扩展,并将生成以下警告:
warning: variable length arrays are a C99 feature [-Wvla-extension]
int x[std::numeric_limits<unsigned int>::min()+2] ;
^
在C++11中,std::numeric_limits<unsigned int>::min()+2
是一个编译时常量表达式,不需要VLA扩展。
6. 在C++11中,析构函数会隐式生成noexcept异常规格。
由于在C++11中,用户定义的析构函数具有隐式的noexcept(true)
规格,如noexcept destructors所述,这意味着以下程序:
#include <iostream>
#include <stdexcept>
struct S
{
~S() { throw std::runtime_error(""); } // bad, but acceptable
};
int main()
{
try { S s; }
catch (...) {
std::cerr << "exception occurred";
}
std::cout << "success";
}
std::terminate
,但在C++03中可以成功运行。#include <iostream>
#include <vector>
#include <algorithm>
class Comparators
{
public:
bool operator()(int first, int second)
{
return first < second;
}
};
int main()
{
class ComparatorsInner : public Comparators{};
std::vector<int> compares ;
compares.push_back(20) ;
compares.push_back(10) ;
compares.push_back(30) ;
ComparatorsInner comparatorInner;
std::sort(compares.begin(), compares.end(), comparatorInner);
std::vector<int>::iterator it;
for(it = compares.begin(); it != compares.end(); ++it)
{
std::cout << (*it) << std::endl;
}
}
目前在C++03模式下,clang
允许使用这段代码并发出警告,除非您使用-pedantic-errors
标志,这有点棘手,请参见在线演示。
8. >>在关闭多个模板时不再是非法的
使用>>
来关闭多个模板不再是非法的,但可能会导致在C++03和C+11中产生不同结果的代码。以下示例摘自右角括号和向后兼容性:
#include <iostream>
template<int I> struct X {
static int const c = 2;
};
template<> struct X<0> {
typedef int c;
};
template<typename T> struct Y {
static int const c = 3;
};
static int const c = 4;
int main() {
std::cout << (Y<X<1> >::c >::c>::c) << '\n';
std::cout << (Y<X< 1>>::c >::c>::c) << '\n';
}
而在C++03中的结果是:
0
3
而在C++11中:
0
0
9. C++11 改变了一些 std::vector 构造函数
稍微修改 这个答案 中的代码,可以看到使用以下来自 std::vector 的构造函数:
std::vector<T> test(1);
#include <iostream>
#include <vector>
struct T
{
bool flag;
T() : flag(false) {}
T(const T&) : flag(true) {}
};
int main()
{
std::vector<T> test(1);
bool is_cpp11 = !test[0].flag;
std::cout << is_cpp11 << std::endl ;
}
int x[] = { 2.0 };
类型为 T 的对象或引用的列表初始化定义如下:
其中包含以下重点(强调我的):
此外,还有许多类似的实例在草案C++标准的附录C.2“C++和ISO C++ 2003”中有所涉及。它还包括:否则,如果 T 是类类型,则考虑构造函数。逐个枚举可适用的构造函数,并通过重载决议(13.3、13.3.1.7)选择最佳构造函数。如果需要进行任何参数的缩小转换(请参阅下文),则程序将是非法的
New kinds of string literals [...] Specifically, macros named R, u8, u8R, u, uR, U, UR, or LR will not be expanded when adjacent to a string literal but will be interpreted as part of the string literal. For example
#define u8 "abc"
const char *s = u8"def"; // Previously "abcdef", now "def"
User-defined literal string support [...]Previously, #1 would have consisted of two separate preprocessing tokens and the macro _x would have been expanded. In this International Standard, #1 consists of a single preprocessing tokens, so the macro is not expanded.
#define _x "there"
"hello"_x // #1
Specify rounding for results of integer / and % [...] 2003 code that uses integer division rounds the result toward 0 or toward negative infinity, whereas this International Standard always rounds the result toward 0.
Complexity of size() member functions now constant [...] Some container implementations that conform to C++ 2003 may not conform to the specified size() requirements in this International Standard. Adjusting containers such as std::list to the stricter requirements may require incompatible changes.
Change base class of std::ios_base::failure [...] std::ios_base::failure is no longer derived directly from std::exception, but is now derived from std::system_error, which in turn is derived from std::runtime_error. Valid C++ 2003 code that assumes that std::ios_base::failure is derived directly from std::exception may execute differently in this International Standard.
一个可能有危险的不向后兼容的更改出现在序列容器的构造函数中,例如std::vector
,特别是在指定初始大小的重载中。在C++03中,它们复制了一个默认构造的元素,而在C++11中,它们会默认构造每个元素。
考虑以下示例(使用boost::shared_ptr
,以便在C++03中有效):
#include <deque>
#include <iostream>
#include "boost/shared_ptr.hpp"
struct Widget
{
boost::shared_ptr<int> p;
Widget() : p(new int(42)) {}
};
int main()
{
std::deque<Widget> d(10);
for (size_t i = 0; i < d.size(); ++i)
std::cout << "d[" << i << "] : " << d[i].p.use_count() << '\n';
}
原因是C++03为"指定大小和原型元素"和"仅指定大小"指定了一个重载,如下所示(为简洁起见省略了分配器参数):
container(size_type size, const value_type &prototype = value_type());
这将始终将 prototype
复制到容器中 size
次。当只使用一个参数调用时,它将创建 size
个默认构造元素的副本。
在 C++11 中,这个构造函数的签名被移除并替换为下面这两种重载形式:
container(size_type size);
container(size_type size, const value_type &prototype);
第二个函数与以前相同,创建prototype
元素的size
个副本。然而,第一个函数(现在只处理指定大小参数的调用)会分别进行默认构造每个元素。
我猜测这种变化的原因是C++03重载无法与仅移动元素类型一起使用。但它仍然是一项破坏性的变化,并且很少有文档记录。
deque
容纳十个独立的小部件,而不是共享同一资源的十个小部件。 - Agentlienstd::istream
读取失败的结果已经改变。CppReference很好地总结了这一点:value
不变并设置failbit
标志。(C++11之前)
如果提取失败,则将零写入value
并设置failbit
标志。如果提取结果太大或太小而无法适应value
,则会写入std::numeric_limits<T> :: max()
或std::numeric_limits<T> :: min()
并设置failbit
标志。(自C++11以来)
如果您习惯于新语义,然后必须使用C++03编写,则主要是一个问题。以下做法并不特别好,但在C++11中定义明确:int x, y;
std::cin >> x >> y;
std::cout << x + y;
int x = 1, y = 1; cin >> x >> y; cout << x*y;
。使用C++03时,当无法读取到y
值时,将正确地生成x
值。 - cmaster - reinstate monica这个线程"在运行时,C++03和C++0x之间有哪些差异可以检测到"提供了一些例子(从那个线程复制而来),通过利用C++11引用折叠等方式确定语言差异:
template <class T> bool f(T&) {return true; }
template <class T> bool f(...){return false;}
bool isCpp11()
{
int v = 1;
return f<int&>(v);
}
同时C++11允许将局部类型用作模板参数:
template <class T> bool cpp11(T) {return true;} //T cannot be a local type in C++03
bool cpp11(...){return false;}
bool isCpp0x()
{
struct local {} var; //variable with local type
return cpp11(var);
}
以下是另一个例子:
#include <iostream>
template<class T>
struct has {
typedef char yes;
typedef yes (&no)[2];
template<int> struct foo;
template<class U> static yes test(foo<U::bar>*);
template<class U> static no test(...);
static bool const value = sizeof(test<T>(0)) == sizeof(yes);
};
enum foo { bar };
int main()
{
std::cout << (has<foo>::value ? "yes" : "no") << std::endl;
}
输出:
Using c++03: no
Using c++11: yes
auto
可能会导致这样的情况。 - OMGtechy>>
符号。你可以构造一个情境,使其能够符合两个标准的编译要求。另一个很容易找到变化的例子是在初始化方面。 - chrisauto
会引起这种情况。在旧的含义下,auto
声明需要一个类型名称;而在新的含义下,不允许使用类型名称。” - Keith Thompson