forked from cirosantilli/cpp-cheat
-
Notifications
You must be signed in to change notification settings - Fork 0
/
constexpr.cpp
202 lines (162 loc) · 5.29 KB
/
constexpr.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/*
# 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
}