C++14 一致性改进: constexpr 和 aggregate初始化
原文发表地址] C++14 conformance improvements: constexpr and aggregate initialization
[原文作者] Andrew Pardoe
[原文发表时间] 2017/3/7
C++11 中有2个重要的特性在C++14中进行了小的升级,这具有深远的影响。
1. C++11 中,constexpr 函数体只允许一条return 语句,而在C++14 中,constexpr 函数体允许几乎所有的语句,允许更多的C++ 使用习惯。在core constant expressions on CppReference 与 learn about constexpr on the same site 中,你可以了解到更多的使用规则。
2. C++11 中,如果一个聚合类型所包含的任何一个成员具有默认的成员初始值时,则不能将其初始化为聚合类型。而在C++14 中,C++标准委员会移除了对含有默认初始值的非静态数据成员的这种限制。你可以在aggregate initialization on CppReference 与non-static data member initialization (被称为NSDMI) 中找到更多的信息。
你可以从CppReference site 看到,虽然这些更改都是对标准的小改动(在这些页面上搜索”C++14” 的注释),但是它们对编译器的实现以及开发人员编写代码方面有很大的影响。
· 以前,constexpr 基本上都是关于表达式的,但是在C++14 中对它进行了扩展,它允许写控制流语句。在你可以写的code方面,这种区别是巨大的: 现在不仅可以可以写表达式,你还可以创建我们惯用的方法, 就是编译时值的编译和改变。
· 允许包含非静态数据成员的聚合类型初始化的更改使得需要聚合类型初始化的情况成为可能。
我们很高兴地宣布,Visual studio 2017 的编译器完全支持扩展的constexpr 和聚合类型的非静态数据成员初始化。
扩展的constexpr
我们扩展constexpr的工作就是测试驱动,我们测试了一些库,这些库是使用扩展的constexpr 重要的用户,包括:
- Sprout
- Boost (with MSVC workarounds removed)
- Boost Hana
- Guidelines Support Library (GSL)
- libc++ tests
- Microsoft’s Range-v3 fork
- C++ Standard Libraries
我们通过McCluskey, Plumhall, 与 Perennial 测试套件,以及通过在线调查,在线论坛 与早期从VC 博客发给我们的bug 报告中获取的代码片段构建的测试用例来评估扩展的constexpr。
利用C++14 constexpr 几乎和将constexpr 关键字放在现有函数上一样简单,原因是扩展的constexpr 允许开发人员在他们的constexpr函数中用惯用的C++。
· 局部变量声明和条件语句现在可以在constexpr函数中使用【如下示例1】
· 非递归,在constexpr 函数中允许所有的循环结构(除goto 之外)【如下示例2】
使用迭代而非递归在编译时应该有更好的表现,并且,由于constexpr 函数可以在运行时使用,在运行时使用也会更好。我们已经加强了constexpr 控制流引擎,急于识别一个constexpr 函数能确保评估非法表达式的情况,所以一个类错误可以在constexpr 创建时捕获而不是将库发送给开发人员之后。
constexpr 示例
// Example 1a
enumclass Messages {
`` Hi,
`` Goodbye
};
constexprMessages switch_with_declaration_in_control(``intx)
{
``switch(``intvalue = x)
``{
``case 0: ``returnMessages::Hi;
``default`` : ``returnMessages::Goodbye;
``}
}
// Example 1b
constexprbooleven(``intx) {
``if(x % 2 == 0)
``returntrue``;
``returnfalse``;
}
// Example 2a
constexprintsimple_count_up_do_loop(``intx) {
``inti = 0;
``do
{
``++i;
`` } ``while(i < x);
``returni;
}
// Example 2b
constexprintmultiple_initializers_multiple_incr_count_up(``intx) {
``inthigher = 0;
``for(``autoi = 0, j = 1; i <= x; ++i, j++)
``higher = j;
``returnhigher;
}
// Negative test cases
constexprboolalways(``intx) {
``returnx;
}
constexprintguaranteed_to_hit_true_path() {
``if(always(1))
``throw"OH NO"`` ; ``// illegal expression, guaranteed to be hit
``return0;
}
constexprintguaranteed_to_hit_false_path() {
``if(42 * 2 - 84)
``return1;
``else
``throw"OH NO"`` ; ``// illegal expression, guaranteed to be hit
``return0;
}
constexprintguaranteed_to_evaluate_while_loop() {
``while(always(33)) {
``newint`` (0); ``// illegal expression, guaranteed to be hit
``}
``return0;
}
constexprintguaranteed_to_evaluate_for_loop() {
``for(; always(22); )
``newint`` (); ``// illegal expression, guaranteed to be hit
``return0;
}
NSDMI for aggregates
聚合类型的非静态数据成员的更改的范围更为有限,但它会自动改进现有的许多代码。在C++11中,如果有默认成员初始化,那么这个类就不是聚合类,因此,聚合初始化就不合法。
例如:
struct S {
int i = 1;
int j = 2;
};
S s1; // OK; calls the default constructor, which initializes 'i' and 'j' to 1 and 2.
S s2{}; // OK; not aggregate initialization because S is not an aggregate; calls the default constructor.
s3{42}; // Error; S is not an aggregate and there is no appropriate constructor. |
在C++14 中,S 现在被认为是聚合类类型,所以可以使用聚合初始化:
S s4{}; ``// OK with C++14; aggregate initialization; no initializer is provided for 'i' or 'j', so their respective default member initializers will be used to initialize them to 1 and 2.
S s5{42}; ``// OK with C++14; aggregate initialization; 'i' is explicitly initialized to 42 and no initializer is provided for 'j', so its default member initializer will be used to initialize it to 2.
|
结束语
和往常一样,我们欢迎您的反馈。 请通过visualcpp@microsoft.com,Twitter@visualc 或者Microsoft Visual Cpp的Facebook 反馈任何意见。
如果在使用VS 2017中遇到Visual C ++的其他问题,请通过“安装程序”或“Visual Studio IDE”本身的Report a Problem 选项通知我们。 有关建议,请通过UserVoice通知我们。 谢谢!