C++11
This article describes the new features in the C++11 standards.
Table of Contents
- Table of Contents
- Perf
- Lambda Expression
- constexpr
- 自动类型推导和 decltype
- 统一的初始化语法 uniform initialization
- deleted 函数和 defaulted 函数
- nullptr
- 委托构造函数
- 右值引用
- C++11 的标准库
- 总结
- Tips
- 参考
Perf
vector<string> v;
string s("hello");
v.push_back(s); // push_back(const T&);
v.push_back(s + "world"); // push_back(T&&);
v.emplace_back("hello"); // emplace_back(Args&&...)
v.emplace_back(); // v.push_back(string());
- Use explicit temporaries only when necessary
- For vector
, etc.: - Call
push_back()
for T lvalues/rvalues and braced-init-lists - Call
emplace_back()
for other types, 0 args, and 2+ args- Emplacement can invoke explicit constructors
- Call
new
anddelete
are radioactive- Use
make_shared
andmake_unique
- Use
new[]
anddelete[]
are radioactive- Use
vector
andstring
- Use
- Manual resource management is radioactive
- At the very least, use Boost.ScopeExit/etc
- Ideally, write classes for automatic resource management
- Each automatic resource manager
- should acquire/release exactly one resource, or
- should acquire/release multiple resources very carefully
- Don’t return by
const
value- Inhibits move semantics, doesn’t achieve anything useful
- Don’t
move()
when returning local x by value x- The NRVO(Named Return Value Optimization) and move semantics are designed to work together
- NRVO applicable -> direct construction is optimal
- NRVO inapplicable -> move semantics is efficient
- Don’t return by rvalue reference
- For experts only, extremely rare
- Even the Standardization Committee got burned
- Valid examples: forward,move,declval,get(tuple&&)
- Always use
nullptr
, never use0/NULL
- The type is what matters, not the spelling
- Rely on template argument deduction
- You control its inputs - the function arguments
- Change their types/value categories to affect the output
- Avoid explicit template arguments, unless required
- Required:
forward<T>(t)
,make_shared<T>(a, b, c)
- Wrong:
make_shared<T, A, B, C>(a, b, c)
- Required:
- STL algorithms (and containers) take functors
- function pointers, function objects, lambdas
- NOT pointers to member functions
- NOT operators
less<T>
adapts operator syntax to functor syntax- Lambdas aren’t always better
sort(v.begin(), v.end(), [](const Elem& l, const Elem& r) { return l > r; });
sort(v.begin(), v.end(), greater<Elem>())
map<Key, Value, greater<Key>>
- sometimes lambda is just too verbose
- When to help the compiler
- Casts (avoid when possible, never use C casts)
- Moves (use carefully)
- Are you certain that stealing from lvalues is safe?
- ‘moved-from’==’unspecified state’
- Special: moved-from shared_ptrs/unique_ptrs are empty
move()
is friendly syntax for a value category cast
- enable_if/SFINAE (prefer tag dispatch, static_assert)
- Common theme: Do you have high-level knowledge?
Lambda Expression
Lambda Expression Syntax
[capture](parameters)->return-type {body}
Example:
int main() {
char s[] = "Hello World!";
int nuppercase = 0; // modified by the lambda
for_each(s, s+sizeof(s), [&nuppercase] (char c) {
if (isupper(c)) ++nuppercase;
});
cout << "number of uppercase char = " << nuppercase << endl;
}
[&nuppercase]
means pass nuppercase
by reference, otherwise pass by value.
constexpr
can be used to enable that expressions be evaluated at compile time.
std::numeric_limits<int>::max();
constexpr size_t square(int x) { return x * x; }
自动类型推导和 decltype
在 C++03 中,声明对象的同时必须指明其类型,其实大多数情况下,声明对象的同时也会包括一个初始值,C++11 在这种情况下就能够让你声明对象时不再指定类型了:
auto x=0; //0 是 int 类型,所以 x 也是 int 类型
auto c='a'; //char
auto d=0.5; //double
auto national_debt=14400000000000LL;//long long
这个特性在对象的类型很大很长的时候很有用,如:
void func(const vector<int> &vi) {
vector<int>::const_iterator ci = vi.begin();
// or
const auto it = vi.begin();
}
C++11 也提供了从对象或表达式中“俘获”类型的机制,新的操作符 decltype
可以从一个表达式中“俘获”其结果的类型并“返回”:
const vector<int> vi;
typedef decltype (vi.begin()) CIT;
CIT another_const_iterator;
统一的初始化语法 uniform initialization
C++ 最少有 4 种不同的初始化形式
-
如括号内初始化,见:
std::string s("hello"); int m = int(); //default initialization
-
还有等号形式的:
std::string s = "hello"; int x=5;
-
对于 POD 集合,又可以用大括号:
int arr[4] = {0,1,2,3}; struct tm today = {0};
-
最后还有构造函数的成员初始化:
struct S { int x; S(): x(0) {} };
这些弊端:
- 过多的初始化方式,
- C++03 中不能初始化 POD 数组的类成员, 也不能在使用 new[] 的时候初始 POD 数组
促成了 C++11 的统一大括号初始化方式:
struct C {
C(int i, int j);
int a;
int b;
};
C c {0,0}; // C++11 only. 相当于 C c(0,0);
int* a = new int[3]{1, 2, 3}; // C++11 only
struct T {
T() : a{1,2,3,4} {} // C++11, 初始化数组成员
int a[4];
};
- Note:
- narrowing initializations
- those that reduce precision or where the supplied values gets modified are not possible with braces. This is relying on the actual value of initializers floating-point to integer conversions are always considered narrowing, even 1.0 to 1
int x1(1.1), x2 = 1.2; // OK
int x3{1.3}, x4 = {1.4}; // ERROR: narrowing
char c1{7}; // ok, 7 is an int, but is not narrowing
char c2{255}; // ERROR: narrowing
还有就是对于容器而言,终于可以摆脱 push_back() 调用了,C++11 中可以直观地初始化容器了:
// C++11 container initializer
vector<string> v {"hello", "world", "from", "cpp"};
map<string, string> singers {
{"Will", "+86 (111) 555-8888"},
{"John", "+86 (111) 555-9999"}
};
而类中的数据成员初始化也得到了支持:
class T {
private:
int m_size = 0; // C++11 only
public:
T();
};
std::initializer_list<>
void echo(std::initializer_list<int> values) {
for (auto& v : values)
std::cout << v << ", ";
}
when there are constructors for both a specific number of arguments and an initializer_list, the version with the inirializer_list is preferred.
deleted 函数和 defaulted 函数
像以下形式的函数:
struct T {
T()=default; // C++11
virtual ~T()=default; // C++11
};
叫做 defaulted 函数,=default; 指示编译器生成该函数的默认实现。这有两个好处:一是少敲键盘,二是有更好的性能。
与 defaulted 函数相对的就是 deleted 函数:int func()=delete;
这货有一大用途就是实现 noncopyabe 防止对象拷贝,要想禁止拷贝,用 =deleted 声明一下两个关键的成员函数就可以了:
struct NoCopy {
NoCopy& operator=(const NoCopy&) = delete;
NoCopy (const NoCopy&) = delete;
};
NoCopy a;
NoCopy b(a); // 编译错误,拷贝构造函数是 deleted 函数
nullptr
nullptr 是一个新的 C++ 关键字,它是空指针常量,它是用来替代高风险的 NULL 宏和 0 字面量的。nullptr 是强类型的:<
void f(int); //#1
void f(char *);//#2
//C++03
f(0); //调用的是哪个 f?
//C++11
f(nullptr) //毫无疑问,调用的是 #2
所有跟指针有关的地方都可以用 nullptr,包括函数指针和成员指针:
const char *pc=str.c_str(); //data pointers
if (pc!=nullptr)
cout<<pc<<endl;
int (A::*pmf)()=nullptr; //指向成员函数的指针
void (*pmf)()=nullptr; //指向函数的指针
委托构造函数
C++11 中构造函数可以调用同一个类的另一个构造函数:
class M { //C++11 delegating constructors
int x, y;
char *p;
public:
M(int v) : x(v), y(0), p(new char [MAX]) {} //#1 target
M(): M(0) {cout << "delegating ctor" << end;} //#2 delegating
#2 就是所谓的委托构造函数,调用了真正的构造函数 #1。
右值引用
在 C++03 中的引用类型是只绑定左值的,C++11 引用一个新的引用类型叫右值引用类型,它是绑定到右值的,如临时对象或字面量。
增加右值引用的主要原因是为了实现 move 语义。与传统的拷贝不同,move 的意思是目标对象“窃取”原对象的资源,并将源置于“空”状态。当拷贝一个对象时,其实代价昂贵且无必要,move 操作就可以替代它。如在 string 交换的时候,使用 move 意义就有巨大的性能提升,如原方案是这样的:
void naive_swap(string &a, string &b) {
string temp = a;
a = b;
b = temp;
}
一般而言,这种法案效率较低,因为涉及到申请内存,然后拷贝字符,而 move 就只需要交换两个数据成员,省去了申请、释放内存和拷贝字符数组等一系列操作:
要实现支持 move 的类,需要声明 move 构造函数和 move 赋值操作符,如下:
class Movable {
Movable (Movable&&); //move constructor
Movable&& operator=(Movable&&); //move assignment operator
};
C++11 的标准库广泛使用 move 语义,很多算法和容器都已经使用 move 语义优化过了。
C++11 的标准库
除 TR1 包含的新容器 (unordered_set, unordered_map, unordered_multiset, 和 unordered_multimap),还有一些新的库,如正则表达式,tuple,函数对象封装器等。 下面介绍一些 C++11 的标准库新特性:
线程库
从程序员的角度来看,C++11 最重要的特性就是并发了。C++11 提供了 thread 类,也提供了 promise 和 future 用以并发环境中的同步,用 async() 函数模板执行并发任务,和 thread_local 存储声明为特定线程独占的数据,这里(http://www.devx.com/SpecialReports/Article/38883)
C++11 新标准中引入了四个头文件来支持多线程编程,他们分别是<atomic> ,<thread>,<mutex>,<condition_variable>和<future>。
- <atomic>:该头文主要声明了两个类, std::atomic 和 std::atomic_flag,另外还声明了一套 C 风格的原子类型和与 C 兼容的原子操作的函数。
- <thread>:该头文件主要声明了 std::thread 类,另外 std::this_thread 命名空间也在该头文件中。
- <mutex>:该头文件主要声明了与互斥量(mutex)相关的类,包括 std::mutex 系列类,std::lock_guard, std::unique_lock, 以及其他的类型和函数。
- <condition_variable>:该头文件主要声明了与条件变量相关的类,包括 std::condition_variable 和 std::condition_variable_any。
- <future>:该头文件主要声明了 std::promise, std::package_task 两个 Provider 类,以及 std::future 和 std::shared_future 两个 Future 类,另外还有一些与之相关的类型和函数,std::async() 函数就声明在此头文件中。
实例代码:
void taskfunc(int idx) {
cout << "thread " << idx << " run" << endl;
}
#define TCNT 5U // thread count
int main(int , char **) {
vector<thread *> tv;
for (int i = 0; i < TCNT; ++i)
tv.push_back(new thread(taskfunc, i));
for (auto p : tv)
p->join();
for (auto p : tv)
delete p;
return 0;
}
// 运行结果:
% ./test
thread 0 run
thread 1 run
thread 2 run
tthhrreeaadd 43 rruunn
vector<future<string>> v;
auto flip = [](string s)->string { reverse(s.begin(), s.end()); return s; };
v.push_back(async([&flip](string s){return flip(s);}, "hello"));
v.push_back(async([&flip](string s){return flip(s);}, "world"));
v.push_back(async([&flip](){return flip("c++11");}));
for (auto& f : v) cout << f.get() << endl;
互斥量
多个线程同时访问共享资源的时候需要需要用到互斥量,当一个线程锁住了互斥量后,其他线程必须等待这个互斥量解锁后才能访问它。thread提供了四种不同的互斥量:
独占式互斥量non-recursive (std::mutex)
递归式互斥量recursive (std::recursive_mutex)
允许超时的独占式互斥量non-recursive that allows timeouts on the lock functions(std::timed_mutex)
允许超时的递归式互斥量recursive mutex that allows timeouts on the lock functions (std::recursive_timed_mutex)
独占式互斥量
独占式互斥量加解锁是成对的,同一个线程内独占式互斥量在没有解锁的情况下,再次对它进行加锁这是不对的,会得到一个未定义行为。
上面的程序输出结果看起来有点乱(std::cout不是线程安全的),所以我们需要在它们访问共享资源的时候使用互斥量加锁。在线程中std::mutex使用成员函数lock加锁unlock解锁,看起来工作的很好,但这样是不安全的,你得始终记住lock之后一定要unlock,但是如果在它们中间出现了异常或者线程直接退出了unlock就没有执行,因为这个互斥量是独占式的,所以在thread1没有解锁之前,其他使用这个互斥量加锁的线程会一直处于等待状态得不到执行。lock_guard模板类使用RAII手法封装互斥量,在实例化对象的时候帮你加锁,并且能保证在离开作用域的时候自动解锁,所以你应该用lock_guard来帮你加解锁。
mutex m;
void taskfunc(int idx) {
m.lock();
cout << "thread " << idx << " run" << endl;
m.unlock();
}
新的智能指针类
C++98 定义的唯一的智能指针类 auto_ptr 已经被弃用,C++11 引入了新的智能针对类 shared_ptr 和 unique_ptr。它们都是标准库的其它组件兼容,可以安全地把智能指针存入标准容器,也可以安全地用标准算法“倒腾”它们。
新的算法
主要是 all_of()、any_of() 和 none_of(),下面是例子:
#include <algorithm>
// C++11 code
//are all of the elements positive?
all_of(first, first+n, ispositive()); //false
//is there at least one positive element?
any_of(first, first+n, ispositive());//true
// are none of the elements positive?
none_of(first, first+n, ispositive()); //false
还有一个新的 copy_n:
vector<int> vi;
// read 5 integers from stdin
copy_n(istream_iterator<int>(cin), 5, back_inserter(vi));
iota() 算法可以用来创建递增序列,它先把初值赋值给 *first,然后用前置 ++ 操作符增长初值并赋值到给下一个迭代器指向的元素,如下:
#include <algorithm>
int a[5] = {0};
char c[3] = {0};
iota(a, a+5, 10); //changes a to {10,11,12,13,14}
iota(c, c+3, 'a'); //{'a','b','c'}
总结
诚然,C++11 仍缺少一些实用的库如 XML API,socket,GUI、反射——以及自动垃圾收集。然而现有特性已经让 C++ 更安全、高效(是的,效率更高了,可以参见 Google 的 基准测试结果 http://www.itproportal.com/2011/06/07/googles-rates-c-most-complex-highest-performing-language/)以及更加易于学习和使用。
如果觉得 C++ 变化太大了,不必惊恐,花点时间来学习就好了。可能在你融会贯通新特性以后,你会同意 Stroustrup 的观点:C++11 是一门新的语言——一个更好的 C++。
Tips
for-ranged loops
- most of the time, you should use:
for (auto& e : r)
for (const auto& e : r)
- anything else?
- actually want a copy:
for (auto e : r) // intentional copy
- actually want a conversion:
for (uint64_t n : r) // uint32_t -> 64
- proxies might be involved:
for (auto&& e : r) // binds to everything
- Examples:
vector<bool>
- Examples:
- actually want a copy:
- efficiency
for (const auto& p : m) // faster
for (const pair<string, int>& p : m) // may indicate constructs temporary
// if m's value type is pair<const string, int>
// a temporary pair<string, int> is constructed
RVO
- URVO
- Unnamed Return Value Optimization /all paths return rvalues
- NRVO
- Named Return Value Optimization /all paths return same local
Everything else: assume an extra copy
API
Pure API design overhead
string nextLine(istream&);
bool nextLine(istream&, string& s); // faster
Containers
- Appending to containers: cheap
- Concatenating containers: expensive
- Returning containers by value worse than appending
Bitfields
- Built-in bitfields
- CANNOT get/set without a shift
- CANNOT get/set entire store at once
- Inefficient code (vicious circle)
- User-defined bitfields with better primitives
Bitfields<1, 3, 4, 8> bf; // 16 bits
bf.setStore(0); // clear entire bitfield
set<1>(bf, 6); // sets second field to 6
auto x = get<2>(bf); // gets third field
// Support(I): Summation
template<unsigned...> struct Sum;
template<unsigned size>
struct Sum<size> { enum { value = size }; };
template<unsigned size, unsigned... sizes>
struct Sum<size, sizes...> {
enum { value = size + Sum<sizes...>::value }
};
static_assert(Sum<1, 2, 3>::value == 6, "");
// Support(II): Store
template<unsigned bits> struct Store;
template<> struct Store<8> { typedef uint8_t Type; }
template<> struct Store<16> { typedef uint16_t Type; }
template<> struct Store<32> { typedef uint32_t Type; }
template<> struct Store<64> { typedef uint64_t Type; }
// Definition
template<unsigned... sizes>
class Bitfields {
typename Store<Sum<sizes...>::value>::Type store;
public:
template<unsigned pos, unsigned b4, unsigned size, unsigned... more>
friend unsigned getImpl(Bitfields<size, more...>);
...
};
// Getting Field's Value
template<unsigned pos, unsigned... sizes>
unsigned get(Bitfields<sizes...> bf) { return getImpl<pos, 0>(bf); }
template<unsigned pos, unsigned b4, unsigned size, unsigned... sizes>
unsigned getImpl(Bitfields<size, sizes...> bf) {
static_assert(pos <= sizeof...(sizes), "Invalid bitfield access");
if (pos == 0) {
if (size == 1) return (bf.store & (1u << b4)) != 0;
return (bf.store >> b4) & ((1u << size) - 1);
}
return getImpl<pos - (pos ? 1 : 0), b4 + (pos ? size : 0)>(bf);
}
// More bitfield primitives
// set
// getNoShift, setNoShift
// maskAt, mask for a given bitfield
// getStore, setStore
参考
- http://blog.csdn.net/candy060403/article/details/7414456
- http://www.cnblogs.com/hujian/archive/2012/12/10/2810813.html
- 维基百科上关于 C++11 新特性的介绍,中文C++11介绍,英文C++11介绍
- C++之父 Bjarne Stroustrup 的关于 C++11 的 FAQ
- http://www.open-std.org/jtc1/sc22/wg21/
- C++0x/C++11 Support in GCC:http://gcc.gnu.org/projects/cxx0x.html
- What is C++0x:https://www2.research.att.com/~bs/what-is-2009.pdf
- Overview of the New C++:http://www.artima.com/shop/overview_of_the_new_cpp
- Overview of the New C++ (C++0x).pdf:http://ishare.iask.sina.com.cn/f/20120005.html?from=like
- A Brief Look at C++0x:http://www.artima.com/cppsource/cpp0x.html
- Summary of C++11 Feature Availability in gcc and MSVC:http://www.aristeia.com/C++11/C++11FeatureAvailability.htm
- C++ 11: Come Closer:http://www.codeproject.com/Articles/344282/Cplusplus-11-Come-Closer
- C++11 threads, locks and condition variables: http://www.codeproject.com/Articles/598695/Cplusplus11-threads-locks-and-condition-variables
- Move Semantics and Perfect Forwarding in C++11:http://www.codeproject.com/Articles/397492/Move-Semantics-and-Perfect-Forwarding-in-Cplusplus
- http://solarianprogrammer.com/categories/C++11/
- C++11 Concurrency:http://www.baptiste-wicht.com/2012/03/cpp11-concurrency-part1-start-threads/
- http://www.hpl.hp.com/personal/Hans_Boehm/misc_slides/sfacm-cleaned.pdf
- http://en.cppreference.com/w/cpp/thread
- http://isocpp.org/blog/2012/12/c11-a-cheat-sheet-alex-sinyakov
- The Biggest Changes in C++11:http://blog.smartbear.com/c-plus-plus/the-biggest-changes-in-c11-and-why-you-should-care/
- Ten C++11 Features Every C++ Developer Should Use:http://www.codeproject.com/Articles/570638/Ten-Cplusplus11-Features-Every-Cplusplus-Developer
- C++11 – A Glance [part 1 of n]:http://www.codeproject.com/Articles/312029/Cplusplus11-A-Glance-part-1-of-n
- C++11 – A Glance [part 2 of n]:http://www.codeproject.com/Articles/314415/Cplusplus11-A-Glance-part-2-of-n
- C++11(及现代C++风格)和快速迭代式开发:http://mindhacks.cn/2012/08/27/modern-cpp-practices/
- Lambda Functions in C++11 - the Definitive Guide:http://www.cprogramming.com/c++11/c++11-lambda-closures.html
- Better types in C++11 - nullptr, enum classes (strongly typed enumerations) and cstdint:http://www.cprogramming.com/c++11/c++11-nullptr-strongly-typed-enum-class.html
- Rvalue-references-and-move-semantics-in-c++11:http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html
- http://www.gotw.ca/publications/index.htm
- http://www.devx.com/SpecialReports/Door/38865
- Multi-threading in C++0x:http://accu.org/index.php/journals/1584
- C++ 0X feature summary cheat sheat:http://www.iesensor.com/blog/2011/05/31/c-0x-feature-summary-cheat-sheat/
- Multithreading in C++0x part 1: Starting Threads:http://www.justsoftwaresolutions.co.uk/threading/multithreading-in-c++0x-part-1-starting-threads.html
- http://en.cppreference.com/w/cpp/thread
- http://www.cplusplus.com/reference/multithreading/