C _Constness - RicoJia/notes GitHub Wiki
========================================================================
========================================================================
-
const (run-time constant)
- const in a function is a promise for not changing the value of a variable.
void foo(int i); const int j = 1; foo(j); //error: you're violating the promise to keep foo constant, but use const_cast instead.
- const can be located in read-only memory.
- const in a function is a promise for not changing the value of a variable.
-
An expression (series of operands and operators, that can be evaluated directly) that can be evaluated at compilation time. It can be used as a constant.Essentially, it's equivalent to copying code
- motivation: it's possible for it to be evaluated during compile time or runtime. A pure runtime value is not allowed here.
-
So you need to make sure your value needs to be know during compile time, before run time for sure. Otherwise, they are evaluated at runtime. Benefits: you can get more code compiled before run time (ex, int x = 3)!
-
const vs constexpr:
- const members can be initialized during compile time or run time, while constexpr is available during compile time.
- sometimes
const
may be converted to compile time constant.
-
const discards qualifiers
: const object calling non-const functionclass Bar{ public: int baz(){} }; foo(const Bar&bar ){ bar.baz(); // needs const qualifier, like int baz() }
========================================================================
========================================================================
-
Basics
- Motivation: computation can be done during compile time!
- Criterion for constexpr variables:
- Must be a literal type (one of the following):
- void
- POD
- reference
- array of literal type.
- class that:
- trivial dtor (dtor that performs no action)
- ctor:
- constexpr
- aggregate (all non-static members are public, no customized ctor, no base class, no virtual function)
- Must be assigned during construction
- Ctor params: all literal values, constexpr variables, const expr functions, "usable" const values.
-
usable consts
const of integral/ enum type. double is not allowed for historical reasons.// Basic case: const must be initialized const i; // wrong // Case 1: not const at all int i = 5; constexpr auto j = i; // Case 2: const is not usable for constexpr constexpr double k = 3.0; // fine const double j = 4.5; constexpr double i=j; //Error: j must be of int/enum // const is int, but not usable because not initialized from a const int i = 5; const j = i; constexpr k = j; // Error: j wasn't initialized with const
-
- Must be a literal type (one of the following):
-
constexpr function is very different from constexpr varibles:
-
Constexpr function returns and takes in literal types, built-in and user-defined. (can work with const double, different from constexpr variable)
class Point{ constexpr Point(double x, double y) noexcept : x(x), y(y) {} //OK, because args might be known during compilation. constexpr double getX() const noexcept {return x;} // because ctor is constexpr, we may declare a constexpr object. // Then, we might be able to get X as constexpr as well! } int main(){ constexpr Point p1 (1.0, 2.0); //We can have constexpr because of the constexpr ctor }
- Because Point object ** has
constexpr
ctor, we can use it as an argument, and return type of another constexpr function**// p1 doesn't even have to be const, as long as it has constexpr ctor constexpr Point returnPoint(Point& p1){ return p1; } Point p1(1.0, 2.0); auto mid = returnPoint(p1); // p1 doesn't even have to be const
- Because Point object ** has
-
C++11 has two more restrictions, while C++14 lifted them:. In C++14, most functions can be constexpr, because constexpr functions can be evaluated during runtime, too.
- constexpr functions cannot have void as return type
-
constexpr functions are implicitly const functions.
class Point{ // Not valid in C++ 11, but valid in C++14. 1. not const member function 2. void as return type constexpr void setPoint(double x, double y){x_ = x; y_ = y; } }
- C++ 11 only supports a function returning constexpr has one statement (the return statement, so use ?: and recursion).
constexpr int pow(int base unsigned exp) noexcept{return (exp==0?1:base*pow(base, exp-1));}
-
C++ 14 has lifted more restrictions
- it can be used during run time as well, if at least one variable is not available during compile time
- class with constexpr constructors or functions with constexpr return type
class point{ public: constexpr Point (double x_val = 0, double y_val = 0)noexcept: x(x_val), y(y_val){ } constexpr double xValue()const noexcept{ return x; } private: double x,y; } constexpr Point ret_midpoint(const Point& a, const Point& b) noexcept{ Point mid((a.Xvalue()+b.xvalue())/2.0, (a.Yvalue()+b.Yvalue())/2.0); return mid; } constexpr auto point = ret_midpoint(a, b);
-
-
Cautions
constexpr
is part of interface. So taking it off is hard- constexpr std::string 不能在compile time compile。 但你可用const char[]
- Functions not available at compile time
- math libraries' pow, mod functions are not available during compile time. So don't use them!!
- std:: cout is also not available during run time!!
-
constexpr
variables must be inline, but inline funcs can be evaluated during compile time
-
Usage
- Technically,
int arr[LEN]
, LEN has to beconstexpr
, and not justconst
- Other situations that require constant expression:
- integral type
- an array bounds
const int cn=2; std::array<int, cn> a2;
- enumerator values
- case values
- bit field sizes (like sizeof)
- value template arguments
- static member initializers
- "symbolic constants: " constants that are literals. Typically, #define is used, but please don't do that.
- static_assert() requires constexpr
- simple fix: non-type template params, work for integral.
template<std::size_t N> // template non-type params is a compiletime thing. constexpr void f(){ static_assert(N==42); }
- simple fix: non-type template params, work for integral.
- Technically,