Modern C++:constexpr
视频地址:
constexpr
Two meanings:
- Objects: const + value known during compilation
- Function: return constexpr result if called with constexpr args
constexpr Objects
must initialized with value known during compilation
constexpr std::size_t arraySize // error! no initializer
int sz // non-constexpr variable
constexpr auto arraySize1 = sz // error! sz's value not know
// at compilation
constexpr auto arraySize2 = 10 // okay
Usable in contexts requiring compile-time constants:
std::array<int, sz> data1; // error! sz's value still unknown
// at compilation
std::array<int, arraySize2> data2;// fine arraySize2 is constexpr
const (hence unmodifiable)
arraySize2 = 10 // error!
constexpr Objects vs. const Objects
const objects may be initialized at runtime
int sz // as before
const auto arraySize3 = sz // fine, arraySize3 is const copy of sz
Such objects invalid in contexts requiring compile-time values:
std::array<int, arraySize3> data3;// error! arraySize3 isn't constexpr
All constexpr objects are const, but not all const objects are constexpr.
Bottom line:
-
Need compile-time value => constexpr is proper tool.
-
When constexpr viable, use it.
=> Resulting object usable in more contexts.
C++ const int maxVal = 100 // typically inferior contexpr int maxVal = 100 // typically superior
constexpr Functions
quite different from const functions
- Not limited to member functions
- "Execute" during compilation and return constexpr result if:
=> Arguments are constexpr
=> Result used in constexpr context
- Might (but might not) "execute" during compilation if:
=> Arguments are constexpr
=> Result not used in constexpr context
- Execute at runtime for 1>= non-constexpr argument
=> no need to "overload" on constexpr
constexpr functions take constexpr and non-constexpr args.
Note
constexpr functions might not return const results
constexpr functions might not "execute" during compilation
client view
constexpr int pow(int base, int exp) noexcept {
constexpr auto numConds = 5;
std::array<int, pow(3, numConds)> result;
Note:
- pow called with constexpr values in a constexpr context
=> Guarantees compile-time evaluation
pow also callable at runtime
auto base = readFromDB("base"); // runtime call
auto exp = readFromDB("exponent"); // runtime call
auto baseToExp = pow(base, exp); // runtime call
constexpr functions are constrained
- May take and return only literal types
=> Essentially types where values compile-time-computable
- Implementation restricted
=> Restriction differ for C++11 and C++14
In C++11, exactly one executable statement: return
- But :? emulates if/else and recursion emulates iteration, so:
constexpr int pow(int base, int exp) noexcept
return (exp == 0? 1 : base * pow(base, exp-1));
C++14 restrictions much looser
- Multiple statements okay
- local variables of literal types okay
- Iteration statements okay
- Etc
// C++14 only
constexpr int pow(int base, int exp) noexcept
auto result = 1;
for(int i = 0; i < exp; i++) result *= base;
return result;
Literal UDTs
Literal types can be user-defined!
class Point
public:
constexpr Point(double xVal, double yVal) noexcept
:x_{ xVal }, y_{ yVal }
constexpr double xValue() const noexcept { return x_; }
constexpr double yValue() const noexcept { return y_; }
constexpr void setX(double xNew) noexcept { x_ = xNew; } // C++14 only
constexpr void setY(double yNew) noexcept { y_ = yNew; } // C++14 only
private:
double x_, y_;
constexpr Point(2.0, 3.2);
constexpr Point midpoint(const Point& p1, const Point& p2) noexcept
return { (p1.xValue() + p2.xValue()) / 2,
(p1.yValue() + p2.yValue()) / 2 };
// init constexpr object result of constexpr function
constexpr auto mid = midpoint(p1, p2);
In C++14, even setters can be constexpr
constexpr Point reflection(const Point& p) noexcept
Point result;