-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconstexpr-cpp11.cpp
More file actions
130 lines (99 loc) · 4.24 KB
/
Copy pathconstexpr-cpp11.cpp
File metadata and controls
130 lines (99 loc) · 4.24 KB
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
#pragma clang diagnostic ignored "-Wunused-variable"
#pragma clang diagnostic ignored "-Wunused-value"
#define CATCH_CONFIG_MAIN // Tells Catch2 to provide a main()
#include "../catch/catch_amalgamated.hpp"
#include "utils.h"
using namespace std;
// constexpr
// - 1. for fns: may evaluate at compile time (forced if used in constant
// expression context, see item below)
// - 2. for vars: forces compile-time evaluation.
//
// constexpr int square(int n) { return n * n; } // 1
// constexpr int val = square(5); // 2
// // square(5) is guaranteed to run at compile time, val is immutable
//
// Key notes:
// - constexpr: functions, constructors, variables, if constexpr
// - constexpr variables are implicitly const
// - Compile time if possible, runtime if needed — compiler decides
// - C++11: single return only; C++14: loops/locals allowed
// - C++17: if constexpr for compile-time branching; lambdas allowed
// - C++20: std::vector, std::string, virtual functions all supported
// - Does NOT guarantee compile-time — use consteval if that guarantee matters
// - Functions must be defined in headers (like templates) — not .cpp files
// - Preferred over macros and magic numbers for compile-time constants
//// 1. C++11 — introduced
// single return statement only, no loops/locals
constexpr int square(int n) { return n * n; }
// for fns: may evaluate at compile time
constexpr int val = square(5); // 25 — compile time
// for vars: forces compile-time evaluation.
int arr[square(4)]; // ✓ array size must be compile-time constant
//// 2. C++14 — rules relaxed
// loops, locals, multiple statements now allowed
constexpr int factorial(int n) {
int result = 1;
for (int i = 2; i <= n; i++) result *= i;
return result;
}
constexpr int f5 = factorial(5); // 120
//// 3. C++17 — "if constexpr" and "constexpr lambdas"
// 3.a. if constexpr
// - Only valid inside a template — condition must depend on template param
// - works outside of template but it is pointless to use, outside of template
// compiler already knows all types.
// - valuates the branch at compile time — the discarded branch is
// "never" compiled, enabling template specialization without overloads.
// - if cond. forced at compile time unlike regular constexpr w/o if
template<typename T>
auto process(T val) {
if constexpr (std::is_integral_v<T>)
return val * 2; // only compiled for ints
else
return val + 0.5; // only compiled for floats
}
TEST_CASE("conste-1") {
REQUIRE(process(42) == 84);
}
// 3.b. constexpr lambdas also allowed in C++17
// - lambdas implicitly constexpr if body is a constant expression
// - explicit constexpr forces and a compile error if not possible
// - captures are allowed in constexpr lambdas if captured values are constexpr
// implicit constexpr if body qualifies
auto sq = [](int x) { return x * x; };
constexpr int v = sq(5); // ✓ 25 — compiled as constexpr automatically
// explicit constexpr
auto sq_ = [](int x) constexpr { return x * x; };
constexpr int v_ = sq_(5); // ✓
//// 4. C++20 — constexpr everywhere
// std::vector, std::string now constexpr
constexpr int sumVec() {
std::vector<int> v = {1, 2, 3, 4, 5};
int s = 0;
for (int x : v) s += x;
return s;
}
constexpr int total = sumVec(); // 15 — at compile time ✓
// virtual functions can now be constexpr
struct Base {
virtual constexpr int value() { return 1; }
};
//// All versions - practical uses
// compile-time sizes
constexpr size_t BUFFER_SIZE = 1024;
constexpr size_t MAX_USERS = 100;
// prefer over macros and magic numbers
#define MAX_USERS 100 // BAD, macro — no type, no scope, no debugger visibility
int arr_[100]; // BAD, magic number — unexplained raw value
constexpr int MAX_USERS_ = 100; // GOOD, typed, scoped, debuggable ✓
int arr__[MAX_USERS_]; // GOOD
// math helpers
constexpr double PI = 3.14159265358979;
constexpr double toRad(double deg) { return deg * PI / 180.0; }
// type traits / template metaprogramming
template<typename T>
constexpr bool isPointer = std::is_pointer_v<T>;
// static_assert validation
constexpr auto isEven = [](int x) constexpr { return x % 2 == 0; };
static_assert(isEven(4)); // ✓ compile time check