One step closer
继拥抱C++11,一步一步来第一篇和第二篇之后,develop分支又经过了一次改进。现在,编译KlayGE所需要的编译器提升到了vc11、g++4.6和clang 3.4。相比上一次的vc10和g++4.3这样刚开始支持C++11的编译器来说,11和4.6基本支持了所有的C++11特性。所以代码里面可以比较自由地使用C++11,而代码更简单。
全部支持的特性
目前vc11、g++ 4.6和clang 3.4都支持的C++11特性如下,远多于vc10、g++ 4.3和clang 3.0这个组合。
语言核心部分
- Static assertions (N1720)
- Multi-declarator auto (N1737)
- Right angle brackets (N1757)
- auto-typed variables (N1984)
- Extern templates (N1987)
- Rvalue references (N2118)
- Declared type of an expression (N2343)
- Standard Layout Types (N2342)
- Strongly-typed enums (N2347)
- Null pointer constant (N2431)
- New function declarator syntax (N2541)
- Removal of auto as a storage-class specifier (N2546)
- Forward declarations for enums (N2764)
- New wording for C++11 lambdas (N2927)
- Range-based for (N2930)
库部分
- algorithm
- array
- atomic
- cstdint
- functional
- memory
- random
- system_error
- tuple
- type_traits
- unordered_map
- unordered_set
一些例外
有些特性,其实很早以前各个编译器和它们所带的库就已经支持了,但并非无条件支持。所以这里仍需要把它们列为可选,并在必要的时候退回到boost的实现。
chrono和thread
这两个库需要在编译GCC本身的时候就定义了_GLIBCXX_HAS_GTHREADS才能使用。对于MinGW-w64来说,它的threads-posix版本有个用pthread实现的std::thread,所以定义了_GLIBCXX_HAS_GTHREADS,能使用chrono和thread。而threads-win32版本却不能,得用boost的。Android NDK里面,g++4.6也没有定义_GLIBCXX_HAS_GTHREADS,到4.8才开始有。所以目前保留了退回boost的wrapper。
mem_fn
mem_fn是<functional>的一部分,本来早该支持。但经过测试,在Windows上,g++直到4.8所带的mem_fn才支持stdcall,之前的编译能通过,执行的时候崩溃。clang用的是MinGW的libstdc++,所以也是一样要g++ 4.8以上。而其他平台不用stdcall,没这个问题。所以目前也需要在不支持的时候退回boost。
atomic、date_time和system
atomic和system已经用的是C++11的,但boost的thread依赖于这三个库,所以也只能在编译boost的时候启用它们。
未来
我们已经基本实现了转换到C++11的目标。原计划是把vc的最小需求升级到12,但因为12的普及率还没那么高,而且12增加的C++11特性用到的不多,这个版本就暂时只到11。在下一个版本中,编译器要求会升级到vc12、g++ 4.9和clang 3.4。并且会考虑开始使用C++14的特性。
另外,我还发现一个挺有意思的地方,decltype。decltype的spec通常被分为v1.0和v1.1。v1.1修正了v1.0的一个小但重要的bug,在最后一刻才被放入了C++11的标准。vc12的正式版和g++ 4.8.1才支持v1.1,之前都是只有v1.0。
那么它们的区别在什么地方呢?下面的代码:
std::vector<T> v; ... for (std::vector<T>::const_reference i : v) { ... }
用decltype来简化,在v1.0里,就需要写成:
std::vector<T> v; ... typedef decltype(v) v_type; for (v_type::const_reference i : v) { ... }
而在v1.1里,可以直接写:
std::vector<T> v; ... for (decltype(v)::const_reference i : v) { ... }
这样可以少一个typedef,代码干净得多。这个问题在Boost.Typeof里就出现过。当时只有vc上可以这么用,而g++的原生typeof必须要经过一次typedef。我在向boost提交了个bug之后,他们认为是gcc的bug,让我自己找gcc。但后来有人找到了一个workaround,用boost::mpl::identity或者自己写一个identity包一层,就解决了。没想到这对decltype也管用。首先定义一下:
template <typename T> struct identity { typedef T value_type; }; #define KLAYGE_DECLTYPE(x) identity<x>::value_type
接着就可以简化代码了:
std::vector<T> v; ... for (KLAYGE_DECLTYPE(v)::const_reference i : v) { ... }
这个诀窍适用于vc10+和g++ 4.3+,但因为到下一次升级编译器要求的时候,decltype v1.1就已经都支持了,我这里就先不弄,以后再说了。
Comments