Visual Studio 2015 RC版中的C++11常量表达式
[原文发表地址] C++11 Constant Expressions in Visual Studio 2015 RC
[原文发表时间] 2015/4/29 11:41PM
Visual Studio 2015 RC中已经实现了C++11的常量表达式特性,这个特性从2013年11月的CTP版本时就已经处于开发阶段。
这篇文章将会描述一些常量表达式特性的简单应用,以及现有RC版本中的常量表达式特性和之前实现的常量表达式特性有哪些功能上的不同,以及未来对这个特性的一些规划。
什么是常量表达式?
常量表达式特性允许你创建,使用constexpr关键字, 并在编译期将运算结果作为常量。如果在编译期编译器使用可用信息计算并满足所有常量表达式必要的约束条件时,这些将声明为constexpr.
常量表达式用在什么地方?
类型安全
过去的常量表达式,都是通过宏来计算常量值
#define LENGTHOF(x) (sizeof(x) / sizeof(x[0]))
charx[10];
chary[LENGTHOF(x)]; // y also has 10 elements
但是宏并不是安全类型.下面的代码X实际上是一个指针, 但是编译时确变为一个无用的值:
char*x;
chary[LENGTHOF(x)];// No error! Expands to (sizeof(char *) / sizeof(x[0]))
为了避免这样的问题, 我们可以使用常量表达式:
template<typename T, size_t length>
constexpr size_tlengthof(T (&)[length])
{
return length;
}
这个函数就和LENGTHOF宏功能一致了:
char x[10];
char y[lengthof(x)];
这样除了X不是数组时编译器会报错, 编译器还会保护函数不被无效使用。
char *x;
char y[lengthof(x)]; // Error C2784: 'size_t lengthof(T (&)[N])': could not deduce template argument for 'T (&)[N]' from 'char *'
通常来说, 应该尽量使用常量表达式替换随处可见的宏,因为常量表达式会对标准C++代码执行类型检查.
元编程
C++模板系统已经是非常成熟的函数编程语言,因此,它也经常用在执行编译期的复杂计算。但是因为它并没有设计为通用型语言,这也导致模板冗长并难以表达计算逻辑。
举例:由于某种原因,你需要根据类型来求幂运算,你必须写成这样:
#include <type_traits>
template <int x, int n, typename Cond = void>
struct Exp
{
static const int result =Exp<x * x, n / 2>::result;
};
template<int x, int n>
struct Exp<x, n, std::enable_if_t<n % 2 == 1>>
{
static const int result = Exp<x * x, (n - 1) / 2>::result * x;
};
template<int x>
struct Exp<x, 0>
{
static const int result = 1;
};
奇怪的语法,多种案例,隐晦的条件判断不仅妨碍了人们阅读这段代码,而且还使调试变得困难。与此同时,由于浮点数不能用在非类型模板参数, 使得这个求幂函数只能用在整形上, 可想而知局限性有多大。为了提高阅读性和功能性,我们使用常量表达式来替换这段函数代码:
constexpr float exp(floatx,intn)
{
return n == 0 ? 1 :
n % 2 == 0 ? exp(x * x, n / 2) :
exp(x * x, (n - 1) / 2) * x;
};
现在我们使用的是标准的C++表达式,不仅使代码量减少而且更易于阅读,又支持将浮点数作为参数。
已知的问题
当前,隐式定义的构造函数不能自动指定为constexpr,即使构造函数已经满足所有必要条件, 因此:
struct A
{
virtual void f();
};
struct B : A
{
constexpr B() {}; // error C2134 : 'A::A' : call does not result in a constant expression
};
为了避开这个问题, 你需要将类中的构造函数显式定义为constexpr:
struct A
{
virtual void f();
constexpr A() {};
};
constexpr构造函数目前还不能初始化数组类型成员:
struct S
{
int a[5]; // note: 'S::a' was not initialized by the constructor
constexpr S() : a() { } // error C2476: 'constexpr' constructor does not initialize all members
};
intmain()
{
constexpr S b; // error C2127: 'b': illegal initialization of 'constexpr' entity with a non-constant expression
}
我们计划在Visual studio 2015RTM或Update1版本修复类似的bug.
下一步 :
C++ 14的泛化常量表达式特性放宽了一些上述构造函数的约束条件.在C++14中, constexpr函数还可以拥有循环和修改局部变量的语法。我们计划在未来实现c++14的泛化常量表达式特性。
由于我们一直寻求提高visual c++编译器的方法,请在你们的代码中尽可能多的使用这个特性, 通过Microsoft Connect 或Visual Studio feedback tool,请保持发送你们的反馈。你也可以直接通过邮箱地址kaniu@microsoft.com.联系我。