mirror of
https://github.com/libretro/cpp-cheat.git
synced 2025-04-16 02:00:01 +00:00
203 lines
5.3 KiB
C++
203 lines
5.3 KiB
C++
/*
|
|
# constexpr
|
|
|
|
C++11 keyword.
|
|
|
|
`const` variables can either be compile time constants or not.
|
|
|
|
The compiler is able to decide that at compile time, but it may be hard for human
|
|
readers to predict if a given variable is a constexpr of not.
|
|
|
|
Using the constexpr keyword however makes the compiler ensure that the variables are constant expressions,
|
|
so that the compile time constantness is more explicit.
|
|
|
|
Two uses:
|
|
|
|
- variables
|
|
|
|
Means that the value of an expression is known at compile time.
|
|
|
|
- functions
|
|
|
|
The value returned by constexpr functions is known to be a compile time constant.
|
|
|
|
The compiler enforces this by inspecting the function.
|
|
*/
|
|
|
|
#include "common.hpp"
|
|
|
|
#if __cplusplus >= 201103L
|
|
int not_constexpr_func() {
|
|
return 1;
|
|
}
|
|
|
|
constexpr int constexpr_func(int i) {
|
|
return i;
|
|
}
|
|
|
|
/*
|
|
C++11 specifies that the body of a constexrp function must contain a single return statement.
|
|
|
|
Otherwise, it would be too much work for the compiler to do.
|
|
|
|
http://stackoverflow.com/questions/3226211/why-is-it-ill-formed-to-have-multi-line-constexpr-functions
|
|
|
|
C++ 14 lifts it.
|
|
|
|
constexpr functions have several restrictions: in broad terms, they cannot have side effects:
|
|
https://stackoverflow.com/questions/5112305/why-not-to-declare-a-function-constexpr
|
|
which is cool, feels like functional pure functions.
|
|
*/
|
|
#if __cplusplus >= 201402L
|
|
constexpr int constexpr_func_multi_statement(int i) {
|
|
// ERROR: cannot have uninitialized vars.
|
|
//int j;
|
|
// ERROR: cannot call non constexpr functions
|
|
//std::time(NULL);
|
|
return i;
|
|
}
|
|
#endif
|
|
|
|
class MyClass {
|
|
public:
|
|
MyClass() : i(1) {}
|
|
constexpr MyClass(int i) : i(i) {}
|
|
// ERROR: Nah, no overload.
|
|
//MyClass(int i) : i(i) {}
|
|
int i;
|
|
static int member;
|
|
constexpr int f(int j) const { return this->i + j; }
|
|
constexpr int noThis(int j) const { return j + 1; }
|
|
int nonConst() const { return std::time(NULL); }
|
|
// ERROR: constexpr data must be static, or this wouldn't make much sense / be useful, right?
|
|
//constexpr int nonStaticConstexpr;
|
|
};
|
|
|
|
constexpr int ConstexprFactorial(int n) {
|
|
return (n == 1) ? 1 : n * ConstexprFactorial(n - 1);
|
|
}
|
|
|
|
/*
|
|
ERROR: the compiler ensures that the function return is constexpr,
|
|
so this does not compile.
|
|
*/
|
|
/*
|
|
int constexpr constexpr_func_bad(){
|
|
return std::time();
|
|
}
|
|
*/
|
|
#endif
|
|
|
|
int main() {
|
|
#if __cplusplus >= 201103L
|
|
// OK: built-in operators that take constexprs return a constexpr
|
|
{
|
|
constexpr int i = 1 + 1;
|
|
}
|
|
|
|
// OK: it is a constexpr
|
|
{
|
|
constexpr int i = 0;
|
|
constexpr int i2 = i;
|
|
}
|
|
|
|
// OK: the compiler sees that a const initialized by a constexpr is also a constexpr.
|
|
{
|
|
const int i = 0;
|
|
constexpr int i2 = i;
|
|
}
|
|
|
|
// ERROR: for non built-in operators, only constexpr functions can be used.
|
|
{
|
|
//constexpr int i = not_constexpr_func();
|
|
}
|
|
|
|
// constexpr functions only work if all their arguments are constexprs.
|
|
{
|
|
{ constexpr int i = constexpr_func(1); }
|
|
// ERROR
|
|
//{ constexpr int i = constexpr_func(std::time(NULL)); }
|
|
}
|
|
|
|
// constexpr object
|
|
{
|
|
constexpr MyClass myClass(1);
|
|
MyClass myClassNonConst;
|
|
|
|
// ERROR: not using constexpr constructor on constexpr object.
|
|
//{ constexpr MyClass myClass; }
|
|
|
|
// Fine with non constexpr constructor.
|
|
|
|
// Now we can use members further constexpr chains.
|
|
{ constexpr int i = myClass.i; }
|
|
// ERROR
|
|
//{ constexpr int i = myClassNonconst.i; }
|
|
|
|
// Methods that don't use this don't need the constexpr object.
|
|
{ constexpr int i = myClassNonConst.noThis(1); }
|
|
// But if the method uses this, then it needs.
|
|
{ constexpr int i = myClass.f(1); }
|
|
// ERROR
|
|
//{ constexpr int i = myClassNonConst.f(1); }
|
|
|
|
// Can still call non constexpr methods of the const object.
|
|
{ int i = myClass.nonConst(); }
|
|
// ERROR But not initialize constexpre expressions with them.
|
|
//{ constexpr int i = myClass.nonConst(); }
|
|
}
|
|
|
|
// Recursive functions can be constexpr, as long as they fit into one line.
|
|
{
|
|
constexpr int i = ConstexprFactorial(3);
|
|
assert(i == 6);
|
|
}
|
|
|
|
// ERROR: the compiler sees that this is not a constexpr.
|
|
// Avoid relying on this execept for legacy code: always initialize a constexpr from constexprs!
|
|
{
|
|
const int i = std::time(NULL);
|
|
//constexpr int i2 = i + 1;
|
|
}
|
|
|
|
// ERROR: it is not a constexpr
|
|
{
|
|
int i = 0;
|
|
//i = std::time(NULL);
|
|
// We could change i at any time!
|
|
//constexpr int i2 = i + 1;
|
|
}
|
|
|
|
// ERROR: constexprs cannot be modified after initialization
|
|
{
|
|
constexpr int i = 0;
|
|
//i = 1;
|
|
}
|
|
|
|
// constexpr do have addresses. But likely they will inlined when addresses are never taken.
|
|
{
|
|
constexpr int i = 1;
|
|
constexpr int j = 2;
|
|
const int *ip = &i;
|
|
const int *jp = &j;
|
|
assert(*ip == 1);
|
|
assert(*jp == 2);
|
|
assert(ip != jp);
|
|
}
|
|
|
|
// WARN: unitialized constexpr
|
|
{
|
|
//constexpr int i;
|
|
}
|
|
|
|
/*
|
|
cannot have constexpr to complex types
|
|
|
|
TODO rationale
|
|
*/
|
|
{
|
|
//constexpr std::string s = "abc";
|
|
}
|
|
#endif
|
|
}
|