目录
正文
原文链接
http://marknelson.us/2014/09/11/highlights-of-iso-c14/
下面是对你的日常开发有重大影响的C++14新变动,列出了一些示例代码,并讨论何时以及为什么要使用它们。
对auto做进一步的阐述是很有趣的事情。C++仍然是类型安全的,但是类型安全机制越来越多的由编译器来执行,而非程序员自己。
在C++11中,程序员已经开始使用auto来进行声明了。当使用全限定类型名称(fully qualified type name )会让你感到吃惊时(因为太长了),例如,创建迭代器,这时候你会感激auto的出现。新发明的C++代码更加易读:
1 for ( auto ii = collection.begin() ; ...
上面的代码仍然是类型安全的——编译器知道begin()在其所在的上下文中会返回什么类型,因此关于类型ii是什么就不再有问题了,并且对每个使用auto的地方都会进行检查。
在C++ 14中,auto的使用在几个方面进行了扩展。一个非常有意义的地方是返回类型推导(return type deduction)。如果我在函数内部写下这样一行代码:
1 return 1.4;
编译器和我都清楚的知道函数的返回值是double。所以在C++14中,我可以将函数返回类型定义为auto而不是double:
1 auto getvalue() {
这个新特性的细节非常容易被理解。举个例子,如果一个函数有多个返回值,它们需要有相同的类型。代码如下:
看上去应该将返回类型推导为double,但是标准会禁止这种模棱两可的行为,编译器会发出抱怨:
error_01.cpp:6:5: error: 'auto' in return type deduced as 'double' here but deduced as 'int' in
earlier return statement
return 2.0
^
1 error generated.
为什么返回类型推导对于C++程序来说是锦上添花的。首先,有时候你必须返回一个非常复杂的类型,比如在对标准库容器进行搜索的时候返回一个迭代器。auto返回类型使得函数更加易读,易写。其次,这个原因可能不是那么明显,使用auto返回类型能够增强你的重构能力。举个例子,考虑下面的代码:
1 #include <iostream> 2 #include <vector> 3 #include <string> 4 5 struct record { 6 std::string name; 7 int id; 8 }; 9 10 auto find_id(const std::vector<record> &people,11 const std::string &name)12 {13 auto match_name = [&name](const record& r) -> bool {14 return r.name == name;15 };16 auto ii = find_if(people.begin(), people.end(), match_name );17 if (ii == people.end())18 return -1;19 else20 return ii->id;21 }22 23 int main()24 {25 std::vector<record> roster = { {"mark",1},26 {"bill",2},27 {"ted",3}};28 std::cout << find_id(roster,"bill") << "\n";29 std::cout << find_id(roster,"ron") << "\n";30 }
在这个例子中,使find_id返回auto比返回int并没有使我节省多少脑力。但是考虑一下,如果我决定重构record结构体,会发生生么。这次我不在record对象中使用一个整型来唯一标记一个人了,而是使用一个新的GUID类型:
1 struct record {2 std::string name;3 GUID id;4 };
对record对象所做的这个改变会导致一系列的连锁反应,比如函数的返回类型会发生变化。但是如果我对函数返回值使用自动类型推导,编译器会默默的应对这些变化。任何在大型工程上进行开发的C++程序员都会熟悉这个问题。对单一数据结构的修改会导致无休止的对原代码的迭代,对变量,参数以及返回类型的修改。auto的广泛使用能够消减很大一部分这样的工作。
注意:在上面的例子和剩下的章节中,我会创建和使用命名的lambda.我猜测大多数用户在使用类似std::find_if()的lambdas函数时,都会将其定义为匿名inline对象,这是非常便利的使用方式。鉴于页面宽度有限,我认为命名lambda在你的浏览器中更加易读。
所以这种写法你不必特地的模仿,仅仅是更加易读而已。特别在你没有lambda经验的情况下,读起来会简单些。
使用auto作为返回值的立竿见影的效果是reality of it's doppelganger, decltype(auto),还有有了类型推导的规则。现在你可以使用它来自动获取类型信息了,如下面的代码片段:
1 template<typename Container>2 struct finder {3 static decltype(Container::find) finder1 = Container::find;4 static decltype(auto) finder2 = Container::find;5 };
另外一个auto悄悄潜伏的地方是在lambda参数的定义中。使用auto类型声明定义lambda参数同创建模板函数基本相当。lambda会基于参数推导类型来进行特定的实例化。
这对创建可在不同上下文中重用的lambdas来说是很方便的。在下面的简单例子中,我创建了一个lambdas用做标准库函数的谓词(predicate)。在C++11的世界里,我分别为整型加法,字符串加法显示的实例化了一个lambda。
有了泛型lambda,我能用其定义单一的lambda。虽然它的语法不包括关键字template,这仍然是对C++泛型编程的进一步扩展:
1 #include <iostream> 2 #include <vector> 3 #include <string> 4 #include <numeric> 5 6 int main() 7 { 8 std::vector<int> ivec = { 1, 2, 3, 4}; 9 std::vector<std::string> svec = { "red",10 "green",11 "blue" };12 auto adder = [](auto op1, auto op2){ return op1 + op2; };13 std::cout << "int result : "14 << std::accumulate(ivec.begin(),15 ivec.end(),16 0,17 adder )18 << "\n";19 std::cout << "string result : "20 << std::accumulate(svec.begin(),21 svec.end(),22 std::string(""),23 adder )24 << "\n";25 return 0;26 }
产生如下结果:
int result : 10
string result : redgreenblue
即使你对匿名inline lambdas进行实例化,正如如前面讨论的,泛型参数仍然是有用的。当你的数据结构发生了变化或者APIs中的函数被修改了,对泛型lambdas进行调整时只需要重新编译就可以了,不需要重新实现:
1 std::cout << "string result : "2 << std::accumulate(svec.begin(),3 svec.end(),4 std::string(""),5 [](auto op1,auto op2){ return op1+op2; } )6 << "\n";
在C++11中我们必须开始适应lambda捕获特化(lambda capture specification)的概念。这种声明会在创建闭包(closure)的时候对编译器进行引导:lambda定义了一个函数的实例,还有定义在lambda作用域之外的绑定在函数上的变量。
在早期的推导返回类型的例子中,我实现了捕获单个变量名字的一个lambda定义,用做谓词中搜索字符串的源:
1 auto match_name = [&name](const record& r) -> bool {2 return r.name == name;3 };4 auto ii = find_if(people.begin(), people.end(), match_name );
这种特殊的捕获使lambda获取了按引用访问变量的权限。捕获也能按值来执行,在两种情况中,变量的使用方式都会符合C++的直觉。按值捕获意味着lambda在本地变量的拷贝上进行操作,按引用捕获意味着lambda在外围作用域的变量本身进行操作。
这些都很好,但也会有伴随而来的一些局限性。我想委员会感觉需要处理的一件事情是使用move-only语义来初始化捕获变量。
这意味着什么?如果我们认为lambda即将成为参数的接收器,我们想使用move语义捕获外部变量。举个例子,考虑如何使lambda接收一个move-only unique_ptr参数。第一个尝试是按值捕获,失败了:
1 std::unique_ptr<int> p(new int);2 *p = 11;3 auto y = [p]() { std::cout << "inside: " << *p << "\n";};
这会生成一个编译错误因为unique_ptr没有拷贝构造函数——它所想的就是禁止拷贝。
将其改为按引用捕获就能编译通过,但是没有达到预期效果:也就是通过将值move到本地的拷贝来接收参数。最后你可以通过先创建本地变量然后在捕获的引用上调用std::move()来完成,但是效率不高。
通过对捕获语句语法进行修改可以修复这个问题。现在我们不是只声明一个捕获变量,我们也能对其初始化。看下面的例子:
1 auto y = [&r = x, x = x+1]()->int {...}
上面的代码捕获了x的拷贝,同时为x增加了1。这个例子很容易理解,但是我不确定它是否为接收move-only变量捕获了这种新语法的值。使用这个新语法的例子如下:
1 #include <memory> 2 #include <iostream> 3 4 int main() 5 { 6 std::unique_ptr<int> p(new int); 7 int x = 5; 8 *p = 11; 9 auto y = [p=std::move(p)]() { std::cout << "inside: " << *p << "\n";};10 y();11 std::cout << "outside: " << *p << "\n";12 return 0;13 }
在这个例子中,捕获的值p用move语义来初始化,在没有声明本地变量的情况下有效的接收了指针:
inside: 11
Segmentation fault (core dumped)
这个令人讨厌的结果正是你想要的——在p被捕获并且move到lambda中后,代码对p进行了解引用。
初次看到deprecated 属性是在java中,我承认有点嫉妒。对大多数程序员来说代码腐烂(rot)是一个巨大的问题。(曾经鼓励删除代码?但我从来没有这么做过)。这个新属性提供了攻克这个问题的系统级别的方法。
用起来很简单,将标签【[[deprecated]]放在声明之前就可以了,声明可以为类,变量,函数或其他东西。结果像下面这个样子:
1 class2 [[deprecated]] flaky {3 };
当你的程序使用了一个deprecated实体,原本需要编译器做出反应,现在留给了代码实现者。很清楚大多数人希望能够看到某种警告,也能随手把这种warning关掉。举个例子,clang3.4在实例化一个deprecated类的时候会发出以下警告:
dep.cpp:14:3: warning: 'flaky' is deprecated [-Wdeprecated-declarations]
flaky f;
^
dep.cpp:3:1: note: 'flaky' declared here
flaky {
^
C++的属性标记语法看上去有点不熟悉。在属性列表中,[[deprecated]]被放在关键字(如class 或者enum)之后,实体名字之前。
这个标记有另外一种形式,它包含了一个信息参数。由开发人员决定如何写这个信息。clang3.4忽略了这个信息。
看下面的代码片段
1 class 2 [[deprecated]] flaky { 3 }; 4 5 [[deprecated("Consider using something other than cranky")]] 6 int cranky() 7 { 8 return 0; 9 }10 11 int main()12 {13 flaky f;14 return cranky();15 }
它并没有包含error信息:
dep.cpp:14:10: warning: 'cranky' is deprecated [-Wdeprecated-declarations]
return cranky();
^
dep.cpp:6:5: note: 'cranky' declared here
int cranky()
^
有两个新功能不是惊天动地的,但他们确实代表了很好的句法结构的改善。这样的很小的改变改善了代码可读性,进一步减少了bug数量。C++ 程序员现在可以创建一个二进制数字,向已经包含十进制,十六进制以及很少使用的八进制的标准中又添加了一员。二进制数字使用前缀0b后面紧接数字。在美国和英国,我们使用逗号来作为数字分隔符,如:$1,000,000。这种写法真正方便了读者,使得我们的大脑处理很长的数字时更加容易。因为同样的原因C++标准委员会添加了数字分隔符。它们不影响数值,只是通过分块让数字的读写更加容易。
数字分隔符使用什么字符?在C++中基本上每个标点符号都被特定的特性使用了,因此没有很明显的选择。最后的选择是使用单引号字符,使得C++的百万数表示如下:1'000'000.00。记住分隔符对数值没有任何影响,因此百万数也可表示如下:1'0'00'0'00.00。
下面的例子使用了两个新特性:
1 #include <iostream> 2 3 int main() 4 { 5 int val = 0b11110000; 6 std::cout << "Output mask: " 7 << 0b1000'0001'1000'0000 8 << "\n"; 9 std::cout << "Proposed salary: $"10 << 300'000.0011 << "\n";12 return 0;13 }
结果也是你所意料的:
Output mask: 33152
Proposed salary: $300000
c++的其他新特性无需多述。变量模板在变量上对模板的扩展。总会使用到的例子是变量pi<T>的一个实现。当实现为double的时候,变量会返回3.14,当实现为int时,它可能返回3,当实现为string时,可能返回“3.14”或者"pi",这是个很棒的特性,以前是在<limits>中实现的。变量模板的语法和语义和类模板是基本相同的——你无需额外的学习就能使用它们。对constexpr函数的限制放松了,例如,可以有多个返回值,可以在内部使用case和if语句,可以用循环以及其它。这就对能在编译器做的事进行了扩展,为模板的引入插上了翅膀。其他小的特性包括为内存分配指定大小(sized deallocations)和一些语法的整理(tidying)。
通过对语言进行改善来保持语言的流行,C++委员会感觉到了压力,它们已经为另外一个标准进行准备了,也就是C++17。
可能更加有趣的事情是创立一些小组,这些小组可以创建技术规格和文档,虽然不会达到标准的水平,但是会被ISO委员会发布和支持。大概这会更加快速的执行。委员会当前致力于8个部分,包括:
这些技术规格的成功与否必须由是否被采纳和使用来评判。如果我们发现所有实现都使用了这些规格,那么为这个规格建立的新轨道就算成功了。
C/C++一致保持着很好的使用度。现代C++,我们从C++11开始算起,在语言易用性和没有损害性能前提下的安全性有了长足的进步。对于特定类型的工作,很难有理由来将C或者C++替换掉。C++14标准并不像C++11跳跃度这样大,但是它使语言在一个很好的方向上。如果标准委员会在接下来的10年能够在生产率上维持当前的水准,在以性能为导向的应用中C++应该会继续成为被选择的语言。
作者:HarlanC
博客地址:http://www.cnblogs.com/harlanc/
目录
正文
原文链接
http://marknelson.us/2014/09/11/highlights-of-iso-c14/
下面是对你的日常开发有重大影响的C++14新变动,列出了一些示例代码,并讨论何时以及为什么要使用它们。
对auto做进一步的阐述是很有趣的事情。C++仍然是类型安全的,但是类型安全机制越来越多的由编译器来执行,而非程序员自己。
在C++11中,程序员已经开始使用auto来进行声明了。当使用全限定类型名称(fully qualified type name )会让你感到吃惊时(因为太长了),例如,创建迭代器,这时候你会感激auto的出现。新发明的C++代码更加易读:
1 for ( auto ii = collection.begin() ; ...
上面的代码仍然是类型安全的——编译器知道begin()在其所在的上下文中会返回什么类型,因此关于类型ii是什么就不再有问题了,并且对每个使用auto的地方都会进行检查。
在C++ 14中,auto的使用在几个方面进行了扩展。一个非常有意义的地方是返回类型推导(return type deduction)。如果我在函数内部写下这样一行代码:
1 return 1.4;
编译器和我都清楚的知道函数的返回值是double。所以在C++14中,我可以将函数返回类型定义为auto而不是double:
1 auto getvalue() {
这个新特性的细节非常容易被理解。举个例子,如果一个函数有多个返回值,它们需要有相同的类型。代码如下:
看上去应该将返回类型推导为double,但是标准会禁止这种模棱两可的行为,编译器会发出抱怨:
error_01.cpp:6:5: error: 'auto' in return type deduced as 'double' here but deduced as 'int' in
earlier return statement
return 2.0
^
1 error generated.
为什么返回类型推导对于C++程序来说是锦上添花的。首先,有时候你必须返回一个非常复杂的类型,比如在对标准库容器进行搜索的时候返回一个迭代器。auto返回类型使得函数更加易读,易写。其次,这个原因可能不是那么明显,使用auto返回类型能够增强你的重构能力。举个例子,考虑下面的代码:
1 #include <iostream> 2 #include <vector> 3 #include <string> 4 5 struct record { 6 std::string name; 7 int id; 8 }; 9 10 auto find_id(const std::vector<record> &people,11 const std::string &name)12 {13 auto match_name = [&name](const record& r) -> bool {14 return r.name == name;15 };16 auto ii = find_if(people.begin(), people.end(), match_name );17 if (ii == people.end())18 return -1;19 else20 return ii->id;21 }22 23 int main()24 {25 std::vector<record> roster = { {"mark",1},26 {"bill",2},27 {"ted",3}};28 std::cout << find_id(roster,"bill") << "\n";29 std::cout << find_id(roster,"ron") << "\n";30 }
在这个例子中,使find_id返回auto比返回int并没有使我节省多少脑力。但是考虑一下,如果我决定重构record结构体,会发生生么。这次我不在record对象中使用一个整型来唯一标记一个人了,而是使用一个新的GUID类型:
1 struct record {2 std::string name;3 GUID id;4 };
对record对象所做的这个改变会导致一系列的连锁反应,比如函数的返回类型会发生变化。但是如果我对函数返回值使用自动类型推导,编译器会默默的应对这些变化。任何在大型工程上进行开发的C++程序员都会熟悉这个问题。对单一数据结构的修改会导致无休止的对原代码的迭代,对变量,参数以及返回类型的修改。auto的广泛使用能够消减很大一部分这样的工作。
注意:在上面的例子和剩下的章节中,我会创建和使用命名的lambda.我猜测大多数用户在使用类似std::find_if()的lambdas函数时,都会将其定义为匿名inline对象,这是非常便利的使用方式。鉴于页面宽度有限,我认为命名lambda在你的浏览器中更加易读。
所以这种写法你不必特地的模仿,仅仅是更加易读而已。特别在你没有lambda经验的情况下,读起来会简单些。
使用auto作为返回值的立竿见影的效果是reality of it's doppelganger, decltype(auto),还有有了类型推导的规则。现在你可以使用它来自动获取类型信息了,如下面的代码片段:
1 template<typename Container>2 struct finder {3 static decltype(Container::find) finder1 = Container::find;4 static decltype(auto) finder2 = Container::find;5 };
另外一个auto悄悄潜伏的地方是在lambda参数的定义中。使用auto类型声明定义lambda参数同创建模板函数基本相当。lambda会基于参数推导类型来进行特定的实例化。
这对创建可在不同上下文中重用的lambdas来说是很方便的。在下面的简单例子中,我创建了一个lambdas用做标准库函数的谓词(predicate)。在C++11的世界里,我分别为整型加法,字符串加法显示的实例化了一个lambda。
有了泛型lambda,我能用其定义单一的lambda。虽然它的语法不包括关键字template,这仍然是对C++泛型编程的进一步扩展:
1 #include <iostream> 2 #include <vector> 3 #include <string> 4 #include <numeric> 5 6 int main() 7 { 8 std::vector<int> ivec = { 1, 2, 3, 4}; 9 std::vector<std::string> svec = { "red",10 "green",11 "blue" };12 auto adder = [](auto op1, auto op2){ return op1 + op2; };13 std::cout << "int result : "14 << std::accumulate(ivec.begin(),15 ivec.end(),16 0,17 adder )18 << "\n";19 std::cout << "string result : "20 << std::accumulate(svec.begin(),21 svec.end(),22 std::string(""),23 adder )24 << "\n";25 return 0;26 }
产生如下结果:
int result : 10
string result : redgreenblue
即使你对匿名inline lambdas进行实例化,正如如前面讨论的,泛型参数仍然是有用的。当你的数据结构发生了变化或者APIs中的函数被修改了,对泛型lambdas进行调整时只需要重新编译就可以了,不需要重新实现:
1 std::cout << "string result : "2 << std::accumulate(svec.begin(),3 svec.end(),4 std::string(""),5 [](auto op1,auto op2){ return op1+op2; } )6 << "\n";
在C++11中我们必须开始适应lambda捕获特化(lambda capture specification)的概念。这种声明会在创建闭包(closure)的时候对编译器进行引导:lambda定义了一个函数的实例,还有定义在lambda作用域之外的绑定在函数上的变量。
在早期的推导返回类型的例子中,我实现了捕获单个变量名字的一个lambda定义,用做谓词中搜索字符串的源:
1 auto match_name = [&name](const record& r) -> bool {2 return r.name == name;3 };4 auto ii = find_if(people.begin(), people.end(), match_name );
这种特殊的捕获使lambda获取了按引用访问变量的权限。捕获也能按值来执行,在两种情况中,变量的使用方式都会符合C++的直觉。按值捕获意味着lambda在本地变量的拷贝上进行操作,按引用捕获意味着lambda在外围作用域的变量本身进行操作。
这些都很好,但也会有伴随而来的一些局限性。我想委员会感觉需要处理的一件事情是使用move-only语义来初始化捕获变量。
这意味着什么?如果我们认为lambda即将成为参数的接收器,我们想使用move语义捕获外部变量。举个例子,考虑如何使lambda接收一个move-only unique_ptr参数。第一个尝试是按值捕获,失败了:
1 std::unique_ptr<int> p(new int);2 *p = 11;3 auto y = [p]() { std::cout << "inside: " << *p << "\n";};
这会生成一个编译错误因为unique_ptr没有拷贝构造函数——它所想的就是禁止拷贝。
将其改为按引用捕获就能编译通过,但是没有达到预期效果:也就是通过将值move到本地的拷贝来接收参数。最后你可以通过先创建本地变量然后在捕获的引用上调用std::move()来完成,但是效率不高。
通过对捕获语句语法进行修改可以修复这个问题。现在我们不是只声明一个捕获变量,我们也能对其初始化。看下面的例子:
1 auto y = [&r = x, x = x+1]()->int {...}
上面的代码捕获了x的拷贝,同时为x增加了1。这个例子很容易理解,但是我不确定它是否为接收move-only变量捕获了这种新语法的值。使用这个新语法的例子如下:
1 #include <memory> 2 #include <iostream> 3 4 int main() 5 { 6 std::unique_ptr<int> p(new int); 7 int x = 5; 8 *p = 11; 9 auto y = [p=std::move(p)]() { std::cout << "inside: " << *p << "\n";};10 y();11 std::cout << "outside: " << *p << "\n";12 return 0;13 }
在这个例子中,捕获的值p用move语义来初始化,在没有声明本地变量的情况下有效的接收了指针:
inside: 11
Segmentation fault (core dumped)
这个令人讨厌的结果正是你想要的——在p被捕获并且move到lambda中后,代码对p进行了解引用。
初次看到deprecated 属性是在java中,我承认有点嫉妒。对大多数程序员来说代码腐烂(rot)是一个巨大的问题。(曾经鼓励删除代码?但我从来没有这么做过)。这个新属性提供了攻克这个问题的系统级别的方法。
用起来很简单,将标签【[[deprecated]]放在声明之前就可以了,声明可以为类,变量,函数或其他东西。结果像下面这个样子:
1 class2 [[deprecated]] flaky {3 };
当你的程序使用了一个deprecated实体,原本需要编译器做出反应,现在留给了代码实现者。很清楚大多数人希望能够看到某种警告,也能随手把这种warning关掉。举个例子,clang3.4在实例化一个deprecated类的时候会发出以下警告:
dep.cpp:14:3: warning: 'flaky' is deprecated [-Wdeprecated-declarations]
flaky f;
^
dep.cpp:3:1: note: 'flaky' declared here
flaky {
^
C++的属性标记语法看上去有点不熟悉。在属性列表中,[[deprecated]]被放在关键字(如class 或者enum)之后,实体名字之前。
这个标记有另外一种形式,它包含了一个信息参数。由开发人员决定如何写这个信息。clang3.4忽略了这个信息。
看下面的代码片段
1 class 2 [[deprecated]] flaky { 3 }; 4 5 [[deprecated("Consider using something other than cranky")]] 6 int cranky() 7 { 8 return 0; 9 }10 11 int main()12 {13 flaky f;14 return cranky();15 }
它并没有包含error信息:
dep.cpp:14:10: warning: 'cranky' is deprecated [-Wdeprecated-declarations]
return cranky();
^
dep.cpp:6:5: note: 'cranky' declared here
int cranky()
^
有两个新功能不是惊天动地的,但他们确实代表了很好的句法结构的改善。这样的很小的改变改善了代码可读性,进一步减少了bug数量。C++ 程序员现在可以创建一个二进制数字,向已经包含十进制,十六进制以及很少使用的八进制的标准中又添加了一员。二进制数字使用前缀0b后面紧接数字。在美国和英国,我们使用逗号来作为数字分隔符,如:$1,000,000。这种写法真正方便了读者,使得我们的大脑处理很长的数字时更加容易。因为同样的原因C++标准委员会添加了数字分隔符。它们不影响数值,只是通过分块让数字的读写更加容易。
数字分隔符使用什么字符?在C++中基本上每个标点符号都被特定的特性使用了,因此没有很明显的选择。最后的选择是使用单引号字符,使得C++的百万数表示如下:1'000'000.00。记住分隔符对数值没有任何影响,因此百万数也可表示如下:1'0'00'0'00.00。
下面的例子使用了两个新特性:
1 #include <iostream> 2 3 int main() 4 { 5 int val = 0b11110000; 6 std::cout << "Output mask: " 7 << 0b1000'0001'1000'0000 8 << "\n"; 9 std::cout << "Proposed salary: $"10 << 300'000.0011 << "\n";12 return 0;13 }
结果也是你所意料的:
Output mask: 33152
Proposed salary: $300000
c++的其他新特性无需多述。变量模板在变量上对模板的扩展。总会使用到的例子是变量pi<T>的一个实现。当实现为double的时候,变量会返回3.14,当实现为int时,它可能返回3,当实现为string时,可能返回“3.14”或者"pi",这是个很棒的特性,以前是在<limits>中实现的。变量模板的语法和语义和类模板是基本相同的——你无需额外的学习就能使用它们。对constexpr函数的限制放松了,例如,可以有多个返回值,可以在内部使用case和if语句,可以用循环以及其它。这就对能在编译器做的事进行了扩展,为模板的引入插上了翅膀。其他小的特性包括为内存分配指定大小(sized deallocations)和一些语法的整理(tidying)。
通过对语言进行改善来保持语言的流行,C++委员会感觉到了压力,它们已经为另外一个标准进行准备了,也就是C++17。
可能更加有趣的事情是创立一些小组,这些小组可以创建技术规格和文档,虽然不会达到标准的水平,但是会被ISO委员会发布和支持。大概这会更加快速的执行。委员会当前致力于8个部分,包括:
这些技术规格的成功与否必须由是否被采纳和使用来评判。如果我们发现所有实现都使用了这些规格,那么为这个规格建立的新轨道就算成功了。
C/C++一致保持着很好的使用度。现代C++,我们从C++11开始算起,在语言易用性和没有损害性能前提下的安全性有了长足的进步。对于特定类型的工作,很难有理由来将C或者C++替换掉。C++14标准并不像C++11跳跃度这样大,但是它使语言在一个很好的方向上。如果标准委员会在接下来的10年能够在生产率上维持当前的水准,在以性能为导向的应用中C++应该会继续成为被选择的语言。
联系客服