Visual C++ Nov 2013 CTP
微软在这周发布了Visual C++ Nov 2013 CTP,包含了下面这些C++11、C++14和C++/CX新特性:
- Implicit move special member function generation (thus also completing =default)
- Reference qualifiers on member functions (a.k.a. “& and && for *this”)
- Thread-safe function local static initialization (a.k.a. “magic statics”)
- Inheriting constructors
- alignof/alignas
- __func__
- Extended sizeof
- constexpr (except for member functions)
- noexcept (unconditional)
- C++14 decltype(auto)
- C++14 auto function return type deduction
- C++14 generic lambdas (with explicit lambda capture list)
- (Proposed for C++17) Resumable functions and await
Implicit move special member function generation
在C++03中default关键字用于在switch语句中声明default case。在C++11中增加了让编译器为类生成默认构造函数、移动构造函数、赋值操作符、移动操作符和析构函数的功能。
比如在C++11中可以这样声明一个wrapper类
1 2 3 4 5 6 7 8 9 10 11
| struct wrapper { wrapper() = default; explicit wrapper(const std::string& str) : str_(str) {} wrapper(const wrapper&) = default; wrapper(wrapper&&) = default; wrapper& operator=(const wrapper&) = default; wrapper& operator=(wrapper&&) = default; ~wrapper() = default; private: std::string str_; };
|
对于其中的=default编译器会为我们生成如下等价的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| struct wrapper { wrapper() : str_() {} explicit wrapper(const std::string& str) : str_(str) {} wrapper(const wrapper& other) : str_(other.str_) {} wrapper(wrapper&& other) : str_(std::move(other.str_)) {} wrapper& operator=(const wrapper& other) { this->str_ = other.str_; return *this; } wrapper& operator=(wrapper&& other) { this->str_ = std::move(other.str_); return *this; } ~wrapper() {} private: std::string str_; };
|
在VS2013中对wrapper(wrapper&&) = default;和wrapper& operator=(wrapper&&) = default;是不支持的。这次的CTP中增加了对这两个的支持。
Reference qualifiers on member functions
考虑如下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| struct wrapper { wrapper() = default; void set_inner_string(const std::string& str) { this->str_ = str; std::cout << "set_inner_string(const std::string& str)" << std::endl; } void set_inner_string(std::string&& str) { this->str_ = std::move(str); std::cout << "set_inner_string(std::string&& str)" << std::endl; } const std::string& get_inner_string() const { return this->str_; } private: std::string str_; };
std::string get_string() { return std::string("http://poxiao.me"); }
wrapper get_wrapper() { wrapper w; w.set_inner_string(get_string()); return w; }
int main() { wrapper w; w.set_inner_string(get_wrapper().get_inner_string()); return 0; }
|
上面的代码在第28行和第35行都调用了wrapper::set_inner_string函数。编译执行输出如下
1 2
| set_inner_string(std::string&& str) set_inner_string(const std::string& str)
|
从结果可以看出28行调用了重载2,而35行调用了重载1。第28行中表达式get_string()是纯右值,所以调用了重载2。第35行中虽然表达式get_wrapper()是纯右值,但wrapper::get_inner_string()返回的却是左值,所以调用了重载1。事实上35行的get_wrapper()的返回值其生命期在该语句结束后就结束了,它的成员变量str_也是如此,所以从性能角度考虑我们是希望能够在这个地方将get_wrapper()返回值对象内部的str_移动给对象w内部的str_的。这需要wrapper::get_inner_string()能够在对象(*this)为纯右值的情况下调用返回纯右值的重载,在对象为左值的时候调用返回左值的重载。C++11中提供了这项特性,此次的CTP支持这项特性。只需将wrapper::get_inner_string修改为如下两个重载版本
1 2 3 4 5 6
| const std::string& get_inner_string() const & { return this->str_; } std::string&& get_inner_string() && { return std::move(this->str_); }
|
编译执行输出如下
1 2
| set_inner_string(std::string&& str) set_inner_string(std::string&& str)
|
Thread-safe function local static initialization
C++11要求函数局部的static变量初始化需是线程安全的,如果并发线程执行到一个函数发现static变量正在初始化,那么它必须等待该变量初始化完成。利用这个特性,使用C++11可以很简单的实现懒惰初始化
的单例模式。
1 2 3 4
| static singleton& get_instance() { static singleton s; return s; }
|
Inheriting constructors
在继承关系中,有时候子类仅仅是扩展了基类的成员方法并没有增加新的成员变量,这种情况下对于子类来说基类的构造函数往往已经足够用了。但在C++03中是没有办法让子类继承基类的构造函数的。如果你有一个基类base如下
1 2 3 4 5 6 7
| class base { int a_; int b_; public: base(int a, int b) : a_(a), b_(b) {} };
|
当derived继承自base需要显示的再将构造函数写一次
1 2 3 4 5
| class derived : public base { public: derived(int a, int b) : base(a, b) {} };
|
在C++11中可以通过using关键字让子类继承基类的构造函数
1 2 3 4 5
| class derived : public base { public: using base::base; };
|
这样derived就相当于有了一个构造函数derived::derived(int, int)。
alignof/alignas
alignof用于获取类型的对齐要求,alignas用于指定类型或对象的对齐要求。
__func__
__func__原是C99的特性,C++11对其进行兼容支持。在C++11中对于每个函数会提供一个预定义的变量__func__,其定义形式同
1
| static const char __func__[] = "function-name";
|
其中function-name
是实现定义的字符串,标准未作要求。在Visual C++ Compiler Nov 2013 CTP编译执行如下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| namespace ns { struct s { s() { std::cout << __func__ << std::endl; } template<typename T> void func(T) const { std::cout << __func__ << std::endl; } }; }
int main() { std::cout << __func__ << std::endl; [](){ std::cout << __func__ << std::endl; }(); ns::s s; s.func(1); return 0; }
|
输出
如果这个预定义变量未被使用,则对应的字符串毋须存在于程序映像中。标准中还给出了下面的例子
1 2 3 4 5
| struct S { S() : s(__func__) { } const char* s; }; void f(const char* s = __func__);
|
Extended sizeof
假设有类s声明如下
1 2 3 4
| struct s { static int si; int i; };
|
在C++03中sizeof不能作用于非静态的数据成员。
1 2
| sizeof(s::si); sizeof(s::i);
|
在C++11中允许sizeof作用于非静态的数据成员
1 2
| sizeof(s::i); sizeof(s::i + 1);
|
这次的CTP更新在实际测试时发现虽然sizeof(s::i)可以编译通过,但对于sizeof(s::i + 1)依然无法编译通过。
constexpr
在C++03中如果我们想在编译期使用递归的方式计算两个指定数值中指定步长的累加和一种可行的办法是使用TMP
(模板元编程)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <iostream>
template<int tN1, int tN2, int tStep> struct accumulate { enum { value = accumulate<tN1, tN2 - tStep, tStep>::value + tN2 }; };
template<int tN1,int tStep> struct accumulate<tN1, tN1, tStep> { enum { value = tN1 }; };
int main() { const int sum1 = accumulate<0, 500, 2>::value; const int sum2 = accumulate<500, 0, -2>::value; std::cout << "sum1 = " << sum1 << '\n' << "sum2 = " << sum2 << std::endl; }
|
输出
1 2
| sum1 = 62750 sum2 = 62750
|
在C++11中增加了constexpr关键字实现上面的功能可以这么做
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <iostream>
constexpr int accumulate_impl(int num1, int num2, int step) { return num1 + step > num2 ? num1 : num1 + accumulate_impl(num1 + step, num2, step); }
constexpr int accumulate(int num1, int num2, int step) { return step < 0 ? accumulate_impl(num2, num1, -step) : accumulate_impl(num1, num2, step); }
int main() { constexpr int sum1 = accumulate(0, 500, 2); constexpr int sum2 = accumulate(500, 0, -2); std::cout << "sum1 = " << sum1 << '\n' << "sum2 = " << sum2 << std::endl; }
|
[待续…]