C++中的const和constexpr详解

2020-01-06 14:17:54王冬梅

另外,const修饰函数时还会参与到函数的重载中,即通过const对象、const指针或引用调用方法时,优先调用const方法。


class A {
public:
  int &operator[](int i) {
    ++cachedReadCount;
    return data[i];
  }
  const int &operator[](int i) const {
    ++size; // !error
    --size; // !error
    ++cachedReadCount; // ok
    return data[i];
  }
private:
  int size;
  mutable cachedReadCount;
  std::vector<int> data;
};
 
A &a = ...;
const A &ca = ...;
int i = a[0]; // call operator[]
int j = ca[0]; // call const operator[]
a[0] = 2; // ok
ca[0] = 2; // !error

这个例子中,如果两个版本的operator[]有着基本相同的代码,可以考虑在其中一个函数中去调用另一个函数来实现代码的重用(参考Effective C++)。这里我们只能用非const版本去调用const版本。


int &A::operator[](int i) {
  return const_cast<int &>(static_cast<const A &>(*this).operator[](i));
}

其中为了避免调用自身导致死循环,首先要将*this转型为const A &,可以使用static_cast来完成。而在获取到const operator[]的返回值后,还要手动去掉它的const,可以使用const_cast来完成。一般来说const_cast是不推荐使用的,但这里我们明确知道我们处理的对象其实是非const的,那么这里使用const_cast就是安全的。

constexpr

constexpr是C++11中新增的关键字,其语义是“常量表达式”,也就是在编译期可求值的表达式。最基础的常量表达式就是字面值或全局变量/函数的地址或sizeof等关键字返回的结果,而其它常量表达式都是由基础表达式通过各种确定的运算得到的。constexpr值可用于enum、switch、数组长度等场合。

constexpr所修饰的变量一定是编译期可求值的,所修饰的函数在其所有参数都是constexpr时,一定会返回constexpr。


constexpr int Inc(int i) {
  return i + 1;
}
 
constexpr int a = Inc(1); // ok
constexpr int b = Inc(cin.get()); // !error
constexpr int c = a * 2 + 1; // ok

constexpr还能用于修饰类的构造函数,即保证如果提供给该构造函数的参数都是constexpr,那么产生的对象中的所有成员都会是constexpr,该对象也就是constexpr对象了,可用于各种只能使用constexpr的场合。注意,constexpr构造函数必须有一个空的函数体,即所有成员变量的初始化都放到初始化列表中。


struct A {
  constexpr A(int xx, int yy): x(xx), y(yy) {}
  int x, y;
};
 
constexpr A a(1, 2);
enum {SIZE_X = a.x, SIZE_Y = a.y};