├── .gitattributes ├── README.md ├── _config.yml └── tips ├── 182.md ├── 183.md ├── 184.md ├── 185.md ├── 186.md ├── 187.md ├── 188.md ├── 189.md ├── 190.md ├── 191.md ├── 192.md ├── 193.md ├── 194.md ├── 195.md ├── 196.md ├── 197.md ├── 198.md ├── 199.md ├── 200.md ├── 201.md ├── 202.md ├── 203.md ├── 204.md ├── 205.md ├── 206.md ├── 207.md ├── 208.md ├── 209.md ├── 210.md ├── 211.md ├── 212.md ├── 213.md ├── 214.md ├── 215.md ├── 216.md ├── 217.md ├── 218.md ├── 219.md ├── 220.md ├── 221.md ├── 222.md ├── 223.md ├── 224.md ├── 225.md ├── 226.md ├── 227.md ├── 228.md ├── 229.md ├── 230.md ├── 231.md ├── 232.md ├── 233.md ├── 234.md ├── 235.md ├── 236.md ├── 237.md ├── 238.md ├── 239.md ├── 240.md ├── 241.md ├── 242.md ├── 243.md ├── 244.md ├── 245.md ├── 246.md ├── 247.md ├── 248.md ├── 249.md ├── 250.md ├── 251.md ├── 252.md ├── 253.md ├── 254.md ├── 255.md ├── 256.md ├── 257.md ├── 258.md ├── 259.md ├── 260.md ├── 261.md ├── 262.md ├── 263.md ├── 264.md ├── 265.md ├── 266.md ├── 267.md ├── 268.md ├── 269.md ├── 270.md ├── 271.md ├── 272.md ├── 273.md ├── 274.md ├── 275.md ├── 276.md ├── 277.md ├── 278.md ├── 279.md ├── 280.md ├── 281.md ├── 282.md ├── 283.md ├── 284.md ├── 285.md ├── 286.md ├── 287.md ├── 288.md ├── 289.md ├── 290.md ├── 291.md ├── 292.md ├── 293.md ├── 294.md ├── 295.md ├── 296.md ├── 297.md ├── 298.md ├── 299.md ├── 300.md ├── 301.md ├── 302.md ├── 303.md ├── 304.md ├── 305.md ├── 306.md ├── 307.md ├── 308.md ├── 309.md ├── 310.md ├── 311.md ├── 312.md ├── 313.md ├── 314.md ├── 315.md ├── 316.md ├── 317.md ├── 318.md ├── 319.md ├── 320.md ├── 321.md ├── 322.md ├── 323.md ├── 324.md ├── 325.md ├── 326.md ├── 327.md ├── 328.md ├── 329.md ├── 330.md ├── 331.md ├── 332.md ├── 333.md ├── 334.md ├── 335.md ├── 336.md ├── 337.md ├── 338.md ├── 339.md ├── 340.md ├── 341.md ├── 342.md ├── 343.md ├── 344.md ├── 345.md ├── 346.md ├── 347.md ├── 348.md ├── 349.md ├── 350.md ├── 351.md ├── 352.md ├── 353.md ├── 354.md ├── 355.md ├── 356.md ├── 357.md ├── 358.md ├── 359.md ├── 360.md ├── 361.md ├── 362.md ├── 363.md ├── 364.md ├── 365.md ├── 366.md ├── 367.md ├── 368.md ├── 369.md ├── 370.md ├── 371.md ├── 372.md ├── 373.md ├── 374.md ├── 375.md ├── 376.md └── images └── parser.png /.gitattributes: -------------------------------------------------------------------------------- 1 | *.md linguist-documentation 2 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-hacker 2 | markdown: GFM 3 | exclude: 4 | tips 5 | -------------------------------------------------------------------------------- /tips/182.md: -------------------------------------------------------------------------------- 1 |
Info

2 | 3 | * **Did you know about the proposal to add Non-terminal variadic template parameters?** 4 | 5 |

Example

6 | 7 | ```cpp 8 | template 9 | auto foo(Ts..., T t) { return t; } 10 | int main() { 11 | std::cout << foo(1); // prints 1 12 | std::cout << foo(1, 2, 3, 4); // prints 4 13 | std::cout << foo(); // compilation error 14 | } 15 | ``` 16 | 17 | > https://godbolt.org/z/z9jvd4vhx 18 | 19 |

Puzzle

20 | 21 | * **[NTVTP] Can you implement `last_using_{get,apply,inplace_lambda}` functions which will return the last element of given tuple?** 22 | 23 | ```cpp 24 | /* TODO */ 25 | constexpr auto last_using_get(...); 26 | constexpr auto last_using_apply(...); 27 | constexpr auto last_using_inplace_lambda(...); 28 | 29 | int main() { 30 | "tuple - get last"_test = [] { 31 | should("return the only element") = []{ 32 | auto tuple = std::tuple{42}; 33 | 42_i == last_using_get(tuple) and 34 | 42_i == last_using_apply(tuple) and 35 | 42_i == last_using_inplace_lambda(tuple); 36 | }; 37 | should("return the last element with same types") = [] { 38 | auto tuple = std::tuple{4l, 2l, 42l}; 39 | 42_l == last_using_get(tuple) and 40 | 42_l == last_using_apply(tuple) and 41 | 42_l == last_using_inplace_lambda(tuple); 42 | }; 43 | should("return the last element with differen types") = [] { 44 | auto tuple = std::tuple{1, 2.0, "3", 4ul}; 45 | 4_ul == last_using_get(tuple) and 46 | 4_ul == last_using_apply(tuple) and 47 | 4_ul == last_using_inplace_lambda(tuple); 48 | }; 49 | }; 50 | } 51 | ``` 52 | 53 | > https://godbolt.org/z/111hzT 54 | 55 |

Solutions

56 | 57 | ```cpp 58 | constexpr auto last_using_get(const auto& tuple) noexcept 59 | { 60 | using tuple_type = std::decay_t; 61 | return std::get - 1>(tuple); 62 | } 63 | constexpr auto last_using_apply(const auto& tuple) 64 | { 65 | return std::apply([](const auto... args) { return (args, ...); }, tuple); 66 | } 67 | constexpr auto last_using_inplace_lambda(const auto& tuple) { 68 | return [&](){ 69 | return last_using_get(tuple); 70 | }(); 71 | } 72 | ``` 73 | 74 | > https://godbolt.org/z/1P3Mcj 75 | 76 | ```cpp 77 | template 78 | constexpr auto last_using_get(TTuple&& t) -> decltype(auto) { 79 | constexpr auto size = std::tuple_size_v>; 80 | return std::get(std::forward(t)); 81 | } 82 | 83 | template 84 | constexpr auto last_using_apply(TTuple&& t) -> decltype(auto) { 85 | return std::apply( 86 | [](auto&&...args) -> decltype(auto) { 87 | return (std::forward(args), ...); 88 | }, 89 | std::forward(t)); 90 | } 91 | 92 | template 93 | constexpr auto last_using_inplace_lambda(TTuple&& t) -> decltype(auto) { 94 | constexpr auto size = std::tuple_size_v>; 95 | return [&] (std::index_sequence) -> decltype(auto) { 96 | return std::get<(I,...)>(std::forward(t)); 97 | }(std::make_index_sequence{}); 98 | } 99 | ``` 100 | 101 | > https://godbolt.org/z/c5PzYK 102 | 103 |

104 | -------------------------------------------------------------------------------- /tips/191.md: -------------------------------------------------------------------------------- 1 |
Info

2 | 3 | * **Did you know that expression evaluation order is not specified?** 4 | 5 | * https://wg21.link/p0145 6 | 7 |

Example

8 | 9 | ```cpp 10 | int main() { 11 | struct arg { arg(int n) { std::printf("arg%d ", n); } }; 12 | auto il = {arg{1}, arg{2}, arg{3}}; // arg1 arg2 arg3 13 | [](auto...){}(arg{1}, arg{2}, arg{3}); // order unspecified 14 | } 15 | ``` 16 | 17 | > https://godbolt.org/z/jEhj61 18 | 19 |

Puzzle

20 | 21 | * **Can you implement a subroutine `is_evaluating_args_from_left_to_right` which returns { true: if expression arguments are evaluated from left to right, false: otherwise }?** 22 | 23 | ```cpp 24 | /*TODO - is_evaluating_args_from_left_to_right */ 25 | 26 | int main() { 27 | using namespace boost::ut; 28 | 29 | "function arguments evaluation order"_test = [] { 30 | #if defined(__clang__) 31 | expect(is_evaluating_args_from_left_to_right()) << "run-time"; 32 | expect(constant) << "compile-time"; 33 | #else 34 | expect(not is_evaluating_args_from_left_to_right()) << "run-time"; 35 | expect(constant) << "compile-time"; 36 | #endif 37 | }; 38 | } 39 | ``` 40 | 41 | > https://godbolt.org/z/z77eTT 42 | 43 |

Solutions

44 | 45 | ```cpp 46 | constexpr bool is_evaluating_args_from_left_to_right() { 47 | std::array ex = {1,2,3}; 48 | int i=0; 49 | std::array tst; 50 | auto f = [&i, &tst] (auto e) { return tst[i++]=e;}; 51 | [](auto...){}(f(1),f(2),f(3)); 52 | return std::equal(ex.begin(), ex.end(), tst.begin()); 53 | } 54 | ``` 55 | 56 | > https://godbolt.org/z/Gv7P7d 57 | 58 | ```cpp 59 | constexpr auto is_evaluating_args_from_left_to_right() { 60 | auto i = 0; 61 | struct S { 62 | constexpr S(int& i) : first(i++ == 0) {} 63 | bool first; 64 | }; 65 | return [](auto x, auto) { return x.first; }(S{i}, S{i}); 66 | } 67 | ``` 68 | 69 | > https://godbolt.org/z/jr74fT 70 | 71 | ```cpp 72 | constexpr auto is_evaluating_args_from_left_to_right() { 73 | auto i = 0; 74 | auto f = [&]() { return i++; }; 75 | return [](auto lhs, auto rhs) { return lhs < rhs; }(f(), f()); 76 | } 77 | ``` 78 | 79 | > https://godbolt.org/z/va4d8v 80 | 81 | ```cpp 82 | constexpr auto is_evaluating_args_from_left_to_right = [] { 83 | auto result = false; 84 | [](auto...) { } (result = true, result = false); 85 | return not result; 86 | }; 87 | ``` 88 | 89 | > https://godbolt.org/z/oE1Tjj 90 | 91 |

92 | -------------------------------------------------------------------------------- /tips/193.md: -------------------------------------------------------------------------------- 1 |
Info

2 | 3 | * **Did you know that C++20 added support for `constexpr new`?** 4 | 5 | * http://wg21.link/p0784 6 | 7 |

Example

8 | 9 | ```cpp 10 | constexpr auto foo() { 11 | auto p = new int{42}; 12 | delete p; 13 | return true; 14 | } 15 | 16 | constexpr auto bar() { 17 | auto p = new int{42}; 18 | //delete p; 19 | return false; 20 | } 21 | 22 | static_assert(foo()); 23 | static_assert(bar()); // compilation error - allocation performed here was not deallocated 24 | ``` 25 | 26 | > https://godbolt.org/z/oPd7zY 27 | 28 |

Puzzle

29 | 30 | * **Can you implement a simplified version of `unique_ptr` which will take advantage of `constexpr new`?** 31 | 32 | ```cpp 33 | /*TODO - constexpr unique_ptr */ 34 | 35 | template 36 | constexpr auto foo(T value) { 37 | return unique_ptr{new T(value)}; 38 | } 39 | 40 | static_assert(std::is_same_v, decltype(foo(int{}))>); 41 | static_assert(std::is_same_v, decltype(foo(double{}))>); 42 | 43 | static_assert(0 == *foo(0)); 44 | static_assert(42 == *foo(42)); 45 | 46 | static_assert(4.2f == *foo(4.2f)); 47 | static_assert(42.d == *foo(42.d)); 48 | ``` 49 | 50 | > https://godbolt.org/z/rPb85a 51 | 52 |

Solutions

53 | 54 | ```cpp 55 | template 56 | class unique_ptr { 57 | public: 58 | constexpr unique_ptr(T* ptr) : ptr(ptr) {} 59 | constexpr T operator*() { return *ptr; } 60 | constexpr ~unique_ptr() { delete ptr; } 61 | private: 62 | T* ptr = nullptr; 63 | }; 64 | ``` 65 | 66 | > https://godbolt.org/z/z6PcME 67 | 68 | ```cpp 69 | template 70 | struct unique_ptr { 71 | constexpr unique_ptr(T* p) : ptr_(p) {} 72 | constexpr ~unique_ptr() { delete ptr_; } 73 | constexpr const auto& operator*() const { return *ptr_; } 74 | 75 | private: 76 | T* ptr_; 77 | }; 78 | ``` 79 | 80 | > https://godbolt.org/z/rh5r5a 81 | 82 | ```cpp 83 | template 84 | class unique_ptr 85 | { 86 | public: 87 | constexpr unique_ptr(T* value) : _value(value) {} 88 | constexpr T& operator*() {return *_value;} 89 | constexpr ~unique_ptr() {delete _value;} 90 | private: 91 | T* _value; 92 | }; 93 | ``` 94 | 95 | > https://godbolt.org/z/fMEKTo 96 | 97 | ```cpp 98 | template 99 | class unique_ptr { 100 | T* ptr; 101 | public: 102 | constexpr unique_ptr(T* ptr) : ptr(ptr) {} 103 | constexpr std::add_lvalue_reference_t operator*() { return *ptr; } 104 | constexpr ~unique_ptr() { delete ptr; } 105 | }; 106 | ``` 107 | 108 | > https://godbolt.org/z/cGs9PG 109 | 110 | ```cpp 111 | template 112 | struct unique_ptr { 113 | constexpr explicit(true) unique_ptr(T *arg) : val(arg) {} 114 | constexpr ~unique_ptr() { delete val; } 115 | 116 | [[nodiscard]] constexpr T operator*() const noexcept { return *val; } 117 | 118 | T *val; 119 | }; 120 | ``` 121 | 122 | > https://godbolt.org/z/v1abT6 123 | 124 |

125 | -------------------------------------------------------------------------------- /tips/195.md: -------------------------------------------------------------------------------- 1 |
Info

2 | 3 | * **Did you know that C++20 added support for `[[no_unique_address]]` attribute?** 4 | 5 | * http://wg21.link/p0840 6 | 7 |

Example

8 | 9 | ```cpp 10 | struct empty {}; 11 | struct ebco : empty {}; // Empty Base Class Optimization (EBCO) 12 | 13 | static_assert(sizeof(empty) == 1); 14 | static_assert(sizeof(ebco) == 1); 15 | 16 | struct no_unique_address { 17 | [[no_unique_address]] struct {} empty; 18 | }; 19 | 20 | static_assert(sizeof(no_unique_address) == 1); 21 | ``` 22 | 23 | > https://godbolt.org/z/P9KK84 24 | 25 |

Puzzle

26 | 27 | * **Can you add an optional field of type `T` to `foo` only when `Enable == true`?** 28 | 29 | * Preferably by leveraging `[[no_unique_address]]` attribute 30 | 31 | ```cpp 32 | template 33 | struct [[gnu::packed]] foo { 34 | int i{}; 35 | bool b{}; 36 | /*TODO - optional field of type T when Enabled */ 37 | }; 38 | 39 | static_assert(sizeof(int) + sizeof(bool) == sizeof(foo)); 40 | static_assert(sizeof(int) + sizeof(bool) + sizeof(int) == sizeof(foo)); 41 | 42 | struct bar{}; 43 | static_assert(sizeof(int) + sizeof(bool) == sizeof(foo)); 44 | 45 | struct baz{ int i{}; }; 46 | static_assert(sizeof(int) + sizeof(bool) + sizeof(baz) == sizeof(foo)); 47 | ``` 48 | 49 | > https://godbolt.org/z/sj5bEx 50 | 51 |

Solutions

52 | 53 | ```cpp 54 | namespace detail { 55 | struct disabled{}; 56 | 57 | template 58 | using when_enabled_t = std::conditional_t; 59 | } 60 | 61 | template 62 | struct [[gnu::packed]] foo { 63 | int i{}; 64 | bool b{}; 65 | [[no_unique_address]] detail::when_enabled_t t{}; 66 | }; 67 | ``` 68 | 69 | > https://godbolt.org/z/ov5d6z 70 | 71 | ```cpp 72 | template 73 | struct [[gnu::packed]] foo { 74 | int i{}; 75 | bool b{}; 76 | struct empty{}; 77 | [[no_unique_address]] std::conditional_t c; 78 | }; 79 | ``` 80 | 81 | > https://godbolt.org/z/9rGrTj 82 | 83 | ```cpp 84 | template 85 | struct [[gnu::packed]] foo { 86 | int i{}; 87 | bool b{}; 88 | }; 89 | 90 | template 91 | struct [[gnu::packed]] foo { 92 | int i{}; 93 | bool b{}; 94 | [[no_unique_address]] T t{}; 95 | }; 96 | ``` 97 | 98 | > https://godbolt.org/z/oor65K 99 | 100 | ```cpp 101 | template 102 | struct [[gnu::packed]] foo { 103 | int i{}; 104 | bool b{}; 105 | [[no_unique_address]] std::conditional_t> t{}; 106 | }; 107 | ``` 108 | 109 | > https://godbolt.org/z/fne13q 110 | 111 |

112 | -------------------------------------------------------------------------------- /tips/196.md: -------------------------------------------------------------------------------- 1 |
Info

2 | 3 | * **Did you know that Lambdas in Unevaluated Context combined with Immediately Invoked Function Expressions (IIFE) can be used to simplify Template Meta-Programming?** 4 | 5 | * http://wg21.link/p0315 6 | 7 |

Example

8 | 9 | ```cpp 10 | static_assert(not std::is_same_v); 11 | static_assert(std::is_same_v); 12 | static_assert(std::is_same_v); 13 | ``` 14 | 15 | > https://godbolt.org/z/cr6Y5P 16 | 17 |

Puzzle

18 | 19 | * **Can you implement an `add_pointer` routine which makes the usage of `decltype(IIFE)` and returns a list of pointer types to: { `value` field if present, `void` otherwise }?** 20 | 21 | * Consider applying C++20 concepts and Design by Introspection for fields detection 22 | 23 | ```cpp 24 | template struct type_list {}; 25 | 26 | template 27 | constinit auto add_pointer = type_list{}; 28 | 29 | struct foo { 30 | int value; 31 | }; 32 | 33 | struct bar { }; 34 | 35 | static_assert(std::is_same_v, decltype(add_pointer<>)>); 36 | static_assert(std::is_same_v, decltype(add_pointer)>); 37 | static_assert(std::is_same_v, decltype(add_pointer)>); 38 | static_assert(std::is_same_v, decltype(add_pointer)>); 39 | ``` 40 | 41 | > https://godbolt.org/z/MKvWj9 42 | 43 |

Solutions

44 | 45 | ```cpp 46 | constexpr auto value_from = [] (auto t) { 47 | if constexpr (requires { t.value; }) { 48 | return t.value; 49 | } 50 | }; 51 | 52 | template 53 | using value_t = std::add_pointer_t()))>; 54 | 55 | template 56 | constinit auto add_pointer = type_list...>{}; 57 | ``` 58 | 59 | > https://godbolt.org/z/PKc9c4 60 | 61 | ```cpp 62 | template 63 | constinit auto add_pointer = type_list{}; 69 | ``` 70 | 71 | > https://godbolt.org/z/ehhxsh 72 | 73 | ```cpp 74 | template 75 | concept HasValue = requires(T t) { t.value; }; 76 | 77 | auto ValueOrVoidPtr = [](const T&) { 78 | if constexpr (HasValue) 79 | return (decltype(T::value)*)(nullptr); 80 | else 81 | return (void*)(nullptr); 82 | }; 83 | 84 | template 85 | constinit auto add_pointer = type_list< decltype(ValueOrVoidPtr(Ts())) ...>{}; 86 | ``` 87 | 88 | > https://godbolt.org/z/1Y7Ehs 89 | 90 | ```cpp 91 | template 92 | constinit auto add_pointer = type_list()))*...>{}; 103 | ``` 104 | 105 | > https://godbolt.org/z/Pn8v7Y 106 | 107 |

108 | -------------------------------------------------------------------------------- /tips/201.md: -------------------------------------------------------------------------------- 1 |
Info

2 | 3 | * **Did you know that `sizeof` operator can be used for efficient math computation?** 4 | 5 | * http://eel.is/c++draft/expr.sizeof 6 | 7 |

Example

8 | 9 | ```cpp 10 | template consteval auto sqr() { 11 | return sizeof(std::byte[n][n]); 12 | } 13 | 14 | static_assert(2*2 == sqr<2>()); 15 | static_assert(10*10 == sqr<10>()); 16 | ``` 17 | 18 | > https://godbolt.org/z/bsvKKT 19 | 20 |

Puzzle

21 | 22 | * **Can you implement an `exponent` variable template which calculates the {x^n} only by leveraging `sizeof` operator?** 23 | 24 | ```cpp 25 | template 26 | constexpr auto exponent = 0; 27 | 28 | static_assert(1 == exponent<1, 1>); 29 | static_assert(1*1*1*1 == exponent<1, 4>); 30 | static_assert(2*2 == exponent<2, 2>); 31 | static_assert(4*4 == exponent<4, 2>); 32 | static_assert(5*5*5*5 == exponent<5, 4>); 33 | static_assert(10*10*10 == exponent<10, 3>); 34 | 35 | static_assert(0 != exponent<1, 2>); 36 | static_assert(1 != exponent<2, 1>); 37 | static_assert(2 != exponent<2, 2>); 38 | ``` 39 | 40 | > https://godbolt.org/z/qqoGxs 41 | 42 |

Solutions

43 | 44 | ```cpp 45 | template 46 | constexpr auto dimensions(auto x) { 47 | if constexpr (N == 0) { 48 | return x; 49 | } else { 50 | return dimensions(std::array{}); 51 | } 52 | } 53 | 54 | template 55 | constexpr auto exponent = sizeof(decltype(dimensions(std::byte{}))); 56 | ``` 57 | 58 | > https://godbolt.org/z/j7Yh46 59 | 60 | ```cpp 61 | template 62 | constexpr auto mult = sizeof(char[X][Y]); 63 | 64 | template 65 | constexpr auto exponent = ((N & 1) == 0) ? mult, exponent> : mult>; 66 | template 67 | constexpr auto exponent = 1; 68 | ``` 69 | 70 | > https://godbolt.org/z/ve6d1v 71 | 72 | ```cpp 73 | template 74 | constexpr auto exponent = sizeof( char[N % 2 ? X : 1][exponent][exponent] ); 75 | 76 | template 77 | constexpr auto exponent = 1; 78 | ``` 79 | 80 | > https://godbolt.org/z/xh6xvf 81 | 82 | ```cpp 83 | template 84 | constexpr auto carr() { 85 | if constexpr(n==0) 86 | return std::array{}; 87 | else 88 | return std::array< decltype(carr()) , x>{}; 89 | } 90 | 91 | template 92 | constexpr auto exponent = sizeof(carr()); 93 | ``` 94 | 95 | > https://godbolt.org/z/19Yvs9 96 | 97 | ```cpp 98 | namespace detail { 99 | 100 | template 101 | [[nodiscard]] consteval auto mult() { 102 | return Val; 103 | } 104 | 105 | template 106 | [[nodiscard]] consteval auto mult() { 107 | return mult(); 108 | } 109 | 110 | [[nodiscard]] consteval auto only_first(auto first, auto...) { 111 | return first; 112 | } 113 | 114 | template 115 | [[nodiscard]] consteval auto exponent_impl(std::index_sequence) { 116 | return mult(); 117 | }; 118 | 119 | } // namespace detail 120 | 121 | template 122 | constexpr auto exponent = 123 | detail::exponent_impl(std::make_index_sequence{}); 124 | ``` 125 | 126 | > https://godbolt.org/z/zevEGe 127 | 128 | ```cpp 129 | template 130 | consteval auto operator,(std::array, U) { return std::array{}; } 131 | 132 | template 133 | constexpr auto exponent = [](std::index_sequence){ 134 | return sizeof((std::array{}, ...)); 135 | }(std::make_index_sequence{}); 136 | ``` 137 | 138 | > https://godbolt.org/z/Yc9nY5 139 | 140 |

141 | -------------------------------------------------------------------------------- /tips/205.md: -------------------------------------------------------------------------------- 1 |
Info

2 | 3 | * **Did you know that C++20 `std::to_array` supports creating from string literals?** 4 | 5 | * http://eel.is/c++draft/array#overview 6 | 7 |

Example

8 | 9 | ```cpp 10 | static_assert(1 == std::size(std::to_array(""))); 11 | static_assert(0 == std::to_array("")[0]); 12 | 13 | static_assert(4 == std::size(std::to_array("foo"))); 14 | static_assert(0 == std::to_array("foo")[3]); 15 | ``` 16 | 17 | > https://godbolt.org/z/5bnPrj 18 | 19 |

Puzzle

20 | 21 | * **Can you implement a `xmas_tree` variable template which creates a Xmas-tree array with a given size?** 22 | 23 | ```cpp 24 | template 25 | constexpr const auto xmas_tree = /*TODO*/std::array{}; 26 | 27 | static_assert( 28 | xmas_tree<3> 29 | == 30 | std::to_array( 31 | " # " 32 | " ### " 33 | "#####" 34 | " # " 35 | ) 36 | ); 37 | 38 | static_assert( 39 | xmas_tree<5> 40 | == 41 | std::to_array( 42 | " # " 43 | " ### " 44 | " ##### " 45 | " ####### " 46 | "#########" 47 | " # " 48 | ) 49 | ); 50 | ``` 51 | 52 | > https://godbolt.org/z/z93hhf 53 | 54 |

Solutions

55 | 56 | ```cpp 57 | template 58 | constexpr const auto xmas_tree = [](std::index_sequence) { 59 | std::array arr{}; 60 | 61 | auto row = [it = arr.begin()](size_t i) mutable { 62 | it = std::fill_n(it, Size - i, ' '); 63 | it = std::fill_n(it, i * 2 - 1, '#'); 64 | it = std::fill_n(it, Size - i, ' '); 65 | }; 66 | 67 | (row(Is + 1), ..., row(1)); 68 | 69 | return arr; 70 | }(std::make_index_sequence()); 71 | ``` 72 | 73 | > https://godbolt.org/z/oKMTqY 74 | 75 | ```cpp 76 | template 77 | constexpr const auto xmas_tree = [](std::index_sequence) 78 | { 79 | static_assert( Size > 0 ); 80 | auto constexpr M = Size * 2 - 1 ; 81 | auto constexpr Center = Size - 1 ; 82 | return std::array { 83 | [](std::index_sequence) { 84 | auto constexpr R = I/ M ; 85 | auto constexpr C = I - R * M; 86 | auto constexpr CL = (R != Size? Size-R-1:Size-1); 87 | auto constexpr CH = (R != Size? Size+R-1:Size-1); 88 | if constexpr( C >= CL && C <= CH ) 89 | return '#'; 90 | else 91 | return ' '; 92 | }(std::index_sequence{}) ... 93 | }; 94 | }(std::make_index_sequence<(Size+1) * (Size * 2 - 1) >{}); 95 | ``` 96 | 97 | > https://godbolt.org/z/5evv39 98 | 99 | ```cpp 100 | template 101 | consteval auto make_xmas_tree() { 102 | constexpr const auto rows = Size + 1; 103 | constexpr const auto cols = (Size * 2) - 1; 104 | std::array tree{}; 105 | tree.fill(' '); 106 | for (auto mid = cols/2, width = 0; 107 | mid < rows * cols; 108 | mid += cols, ++width, width %= Size) { 109 | std::fill_n(&tree[mid - width], width * 2 + 1, '#'); 110 | } 111 | tree.back() = 0; 112 | return tree; 113 | } 114 | 115 | template 116 | constexpr const auto xmas_tree = make_xmas_tree(); 117 | ``` 118 | 119 | > https://godbolt.org/z/fEa9v5 120 | 121 | ```cpp 122 | template 123 | constexpr const auto tree() 124 | { 125 | std::array tree{}; 126 | for(uint32_t r=0; rr ? ' ' : '#'; 130 | else 131 | tree[r*(2*Size-1)+c] = std::abs(c-(Size-1)) != 0 ? ' ' : '#'; 132 | return tree; 133 | } 134 | 135 | template 136 | constexpr const auto xmas_tree = tree(); 137 | ``` 138 | 139 | > https://godbolt.org/z/fjs58z 140 | 141 |

142 | -------------------------------------------------------------------------------- /tips/225.md: -------------------------------------------------------------------------------- 1 |
Info

2 | 3 | * **Did you know about C++23 feature which removes unnecessary ()’s from C++ lambdas?** 4 | 5 | * http://wg21.link/p1102 6 | 7 |

Example

8 | 9 | ```cpp 10 | int main() { 11 | constexpr auto okay_in_cpp20 = [] {}; // okay in C++20 12 | constexpr auto error_in_cpp20 = [] mutable {}; // okay in C++23 13 | constexpr auto error_in_cpp20 = [] -> int { return {};}; // okay in C++23 14 | } 15 | ``` 16 | 17 | > https://godbolt.org/z/4Tjbb8xzr 18 | 19 |

Puzzle

20 | 21 | * **Can you remove unnecessary ()'s from the following lambda expressions?** 22 | 23 | ```cpp 24 | int main() { 25 | constexpr auto l1 = []() {}; 26 | constexpr auto l2 = []()mutable{}; 27 | constexpr auto l3 = []()mutable constexpr{}; 28 | constexpr auto l4 = []()mutable consteval{}; 29 | constexpr auto l5 = []()mutable consteval {}; 30 | constexpr auto l6 = []() -> int { return {}; }; 31 | constexpr auto l7 = []() -> auto { return 0; }; 32 | constexpr auto l8 = []() {}; 33 | constexpr auto l9 = []() {}; 34 | constexpr auto l10 = []() mutable {}; 35 | constexpr auto l11 = []() noexcept {}; 36 | } 37 | ``` 38 | 39 | > https://godbolt.org/z/n6rPMjhaz 40 | 41 |

Solutions

42 | 43 | ```cpp 44 | int main() { 45 | constexpr auto l1 = [] {}; 46 | constexpr auto l2 = [] mutable{}; 47 | constexpr auto l3 = [] mutable constexpr {}; 48 | constexpr auto l4 = [] mutable consteval {}; 49 | constexpr auto l5 = [] mutable consteval {}; 50 | constexpr auto l6 = [] -> int { return {}; }; 51 | constexpr auto l7 = [] -> auto { return 0; }; 52 | constexpr auto l8 = [] {}; 53 | constexpr auto l9 = [] {}; 54 | constexpr auto l10 = [] mutable {}; 55 | constexpr auto l11 = [] noexcept {}; 56 | } 57 | ``` 58 | 59 | > https://godbolt.org/z/ozro3cr53 60 | -------------------------------------------------------------------------------- /tips/230.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know that C++23 added `if consteval`?** 4 | 5 | * http://wg21.link/p1938 6 | 7 |

Example

8 | 9 | ```cpp 10 | #include 11 | 12 | consteval int f(int i) { return i; } 13 | 14 | constexpr int g(int i) { 15 | if consteval { 16 | return f(i) + 1; // ok: immediate function context 17 | } else { 18 | return 42; 19 | } 20 | } 21 | 22 | consteval int h(int i) { 23 | return f(i) + 1; // ok: immediate function context 24 | } 25 | 26 | static_assert(42 + 1 == h(42)); 27 | 28 | int main() { 29 | int i = 42; 30 | //h(i); // error: the value of 'i' is not usable in a constant expression 31 | assert(42 == g(i)); 32 | } 33 | ``` 34 | 35 | > https://godbolt.org/z/rjeodeMoP 36 | 37 |

Puzzle

38 | 39 | * **Can you implement a `add_or_sub` algorithm which adds `args…` to `N` in consteval context and subs `args...` from `N` otherwise?** 40 | 41 | ```cpp 42 | template 43 | constexpr auto add_or_sub = [](const auto... args); // TODO 44 | 45 | int main() { 46 | using namespace boost::ut; 47 | 48 | "add or sub test"_test = [] { 49 | should("add in consteval context") = [] { 50 | expect(constant<0_i == add_or_sub<0>()>); 51 | expect(constant<1_i == add_or_sub<0>(1)>); 52 | expect(constant<2_i == add_or_sub<1>(1)>); 53 | expect(constant<4_i == add_or_sub<1>(1, 2)>); 54 | expect(constant<16_i == add_or_sub<2>(3, 4, 7)>); 55 | }; 56 | 57 | should("sub in non-consteval context") = [] { 58 | expect(0_i == add_or_sub<0>()); 59 | expect(-1_i == add_or_sub<0>(1)); 60 | expect(0_i == add_or_sub<1>(1)); 61 | expect(-2_i == add_or_sub<1>(1, 2)); 62 | expect(-12_i == add_or_sub<2>(3, 4, 7)); 63 | 64 | const auto i = 42; 65 | expect(-44_i == add_or_sub<>(2, i)); 66 | 67 | auto x = 7; 68 | expect(-51_i == add_or_sub<1>(1, x, 2, i)); 69 | }; 70 | }; 71 | } 72 | ``` 73 | 74 | > https://godbolt.org/z/xssv8MY4W 75 | 76 |

Solutions

77 | 78 | ```cpp 79 | template 80 | constexpr auto add_or_sub = [](const auto... args) { 81 | if consteval { 82 | return (N + ... + args); 83 | } else { 84 | return (N - ... - args); 85 | } 86 | }; 87 | ``` 88 | 89 | > https://godbolt.org/z/8vqoPb5dT 90 | 91 | ```cpp 92 | template 93 | constexpr auto add_or_sub = [](const auto... args) { 94 | const auto sum_of_args = (args + ... + 0); 95 | if consteval { 96 | return N + sum_of_args; 97 | } else { 98 | return N - sum_of_args; 99 | } 100 | }; 101 | ``` 102 | 103 | > https://godbolt.org/z/5xerzMjq3 104 | -------------------------------------------------------------------------------- /tips/232.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know that different overloads can have different specifiers?** 4 | 5 | * http://eel.is/c++draft/dcl.spec.general 6 | 7 |

Example

8 | 9 | ```cpp 10 | constexpr auto foo(auto) { return 42; } 11 | consteval auto foo(auto f) requires requires { f.value; } { return f.value; } 12 | 13 | int main(int argc, char**) { 14 | std::cout << foo(argc); // prints 42 15 | 16 | constexpr struct { 17 | int value{88}; 18 | } v; 19 | 20 | std::cout << foo(v); // prints 88 21 | } 22 | ``` 23 | 24 | > https://godbolt.org/z/a9sKb177j 25 | 26 |

Puzzle

27 | 28 | * **Can you implement a function f which for { 0 args: is run-time - returns 42; 1 arg: is run/compile-time, returns arg; >1 args: is compile-time, returns sum of args... }** 29 | 30 | ```cpp 31 | auto f(...); // TODO 32 | 33 | template auto is_compile_time() {} 34 | 35 | int main() { 36 | using namespace boost::ut; 37 | 38 | "run-time call"_test = [] { 39 | expect(42_i == f()); 40 | expect(not [](auto... ts) { return requires { is_compile_time(); }; }()); 41 | }; 42 | 43 | "constexpr call"_test = [] { 44 | "compile-time call"_test = [] { 45 | expect(constant<42_i == f(42)>); 46 | expect(requires { is_compile_time(); }); 47 | 48 | constexpr auto i = 43; 49 | expect(constant<43_i == f(43)>); 50 | }; 51 | 52 | "run-time call"_test = [] { 53 | auto i = 44; 54 | expect(44_i == f(i)); 55 | }; 56 | }; 57 | 58 | "consteval call"_test = [] { 59 | expect(constant<3_i == f(1, 2)>); 60 | expect(constant<6_i == f(1, 2, 3)>); 61 | auto i = 1; 62 | //f(42, i); // should not compile 63 | }; 64 | } 65 | ``` 66 | 67 | > https://godbolt.org/z/EWK873qfv 68 | 69 |

Solutions

70 | 71 | ```cpp 72 | auto f() { return 42; } 73 | constexpr auto f(auto v) { return v; } 74 | template consteval auto f(Args... args) { return (0 + ... + args); } 75 | ``` 76 | 77 | > https://godbolt.org/z/6hM3nbabv 78 | 79 | 80 | ```cpp 81 | auto f() { return 42; } 82 | constexpr auto f(auto arg) { return arg; } 83 | consteval auto f(auto... args) { return (... + args); } 84 | ``` 85 | 86 | > https://godbolt.org/z/qdf3z1Khr 87 | 88 | ```cpp 89 | auto f() { return 42; } 90 | constexpr auto f(auto value) { return value; } 91 | consteval auto f(auto... values) requires (sizeof...(values) > 1) { return (... + values); } 92 | ``` 93 | 94 | > https://godbolt.org/z/8o3WYG8ze 95 | -------------------------------------------------------------------------------- /tips/233.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know that C++20 made `typename` more optional?** 4 | 5 | * http://wg21.link/p0634 6 | 7 |

Example

8 | 9 | ```cpp 10 | template /*typename*/T::type return_type(); // okay 11 | template void void_parameter(/*typename*/T::type); // error: variable or field 'parameter' declared void 12 | template auto auto_parameter(/*typename*/T::type); // okay 13 | 14 | template 15 | struct traits { 16 | using type = /*typename*/ T::type; // okay 17 | }; 18 | ``` 19 | 20 | > https://godbolt.org/z/oMMzfjeKv 21 | 22 |

Puzzle

23 | 24 | * **Can you add/remove required/unnecessary `typename's`** 25 | 26 | ```cpp 27 | template 28 | struct traits { 29 | using type = typename T::type; 30 | }; 31 | 32 | template 33 | struct s : typename T::type {}; 34 | 35 | template typename T::type f1(); 36 | template auto f2() -> typename T::type; 37 | auto f3(auto t) -> typename decltype(t)::type; 38 | 39 | template auto f4(typename T::type); 40 | template bool f5(typename T::type); 41 | template void f6(T::type); 42 | template auto f7(T::type) -> void; 43 | auto f8(auto t) -> typename decltype(t)::type; 44 | ``` 45 | 46 | > https://godbolt.org/z/EEeTh5TWP 47 | 48 |

Solutions

49 | 50 | ```cpp 51 | template 52 | struct traits { 53 | using type = T::type; 54 | }; 55 | 56 | template 57 | struct s : T::type {}; 58 | 59 | template T::type f1(); 60 | template auto f2() -> T::type; 61 | auto f3(auto t) -> decltype(t)::type; 62 | 63 | template auto f4(T::type); 64 | template bool f5(T::type); 65 | template void f6(typename T::type); 66 | template auto f7(typename T::type) -> void; 67 | auto f8(auto t) -> decltype(t)::type; 68 | ``` 69 | 70 | > https://godbolt.org/z/nMsqeaev8 71 | -------------------------------------------------------------------------------- /tips/234.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know about function-try-block and that exceptions caught inside that block are implicitly rethrown?** 4 | 5 | * http://eel.is/c++draft/except.pre#nt:function-try-block 6 | 7 |

Example

8 | 9 | ```cpp 10 | struct foo { 11 | foo() { throw 0; } 12 | }; 13 | 14 | struct bar { 15 | bar() try : foo_{} { 16 | // constructor body 17 | } 18 | catch (...) 19 | { 20 | // exceptions from the initializer list are caught here 21 | // but also re-thrown after this block (unless the program is aborted) 22 | } 23 | 24 | private: 25 | foo foo_; 26 | }; 27 | 28 | int main() try { 29 | bar b{}; 30 | } 31 | catch(...) { 32 | // okay, exception cought here! 33 | } 34 | ``` 35 | 36 | > https://godbolt.org/z/Y4efK573f 37 | 38 |

Puzzle

39 | 40 | * **Can you implement `foo`'s constructor which initializes `Ts...` and sets `exception` to true if any exception is thrown and rethrows it?** 41 | 42 | ```cpp 43 | template 44 | struct ctor_except { 45 | ctor_except() { throw TException{}; } 46 | }; 47 | 48 | template 49 | struct foo : Ts... { 50 | explicit(true) foo(bool& exception); // TODO 51 | }; 52 | 53 | int main() { 54 | using namespace boost::ut; 55 | 56 | "function-try-block"_test = [] { 57 | bool exception{false}; 58 | struct bar { }; 59 | 60 | should("not set exception with empty list") = [=] { 61 | expect(nothrow([&]{ foo<>{mut(exception)}; }) and not exception); 62 | }; 63 | 64 | should("not set exception with non-throwing types") = [=] { 65 | expect(nothrow([&]{ foo{mut(exception)}; }) and not exception); 66 | }; 67 | 68 | should("catch exception if thrown from within the constructor") = [=] { 69 | expect(throws([&]{ foo>{mut(exception)}; }) and exception); 70 | }; 71 | 72 | should("catch exception if thrown from within the constructor with muliple types") = [=] { 73 | expect(throws([&]{ foo>{mut(exception)}; }) and exception); 74 | }; 75 | }; 76 | } 77 | ``` 78 | 79 | > https://godbolt.org/z/s3MzE8xM9 80 | 81 |

Solutions

82 | 83 | ```cpp 84 | template 85 | struct foo : Ts... { 86 | explicit(true) foo (bool& exception) try : Ts{}... { 87 | } catch (...) { 88 | exception = true; 89 | } 90 | }; 91 | ``` 92 | 93 | > https://godbolt.org/z/8Yxr7WbsM 94 | 95 | ```cpp 96 | template 97 | struct foo : Ts... { 98 | explicit(true) foo(bool& exception) try : Ts()... { 99 | exception = false; 100 | } catch(...) { 101 | exception = true; 102 | } 103 | }; 104 | ``` 105 | 106 | > https://godbolt.org/z/dnhcjsYjK 107 | -------------------------------------------------------------------------------- /tips/237.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know about C++2X proposal for the Circle Meta-model for compilation-time meta-programming?** 4 | 5 | * http://wg21.link/p2062 6 | * https://github.com/seanbaxter/circle/blob/master/reflection/README.md 7 | 8 |

Example

9 | 10 | ```cpp 11 | #include 12 | 13 | auto main() -> int { 14 | std::puts("Hello world"); // during run-time 15 | @meta std::puts("Hello circle"); // during compilation-time 16 | } 17 | ``` 18 | 19 | ```sh 20 | Hello circle 21 | ASM generation compiler returned: 0 22 | Hello circle 23 | Execution build compiler returned: 0 24 | Program returned: 0 25 | Hello world 26 | ``` 27 | 28 | > https://godbolt.org/z/bo48q1oWW 29 | 30 |

Puzzle

31 | 32 | * **Can you implement `to_tuple_with_names` which returns `named` fields based on Circle Meta-model?** 33 | 34 | ```cpp 35 | template 36 | struct named { 37 | T value{}; 38 | std::string_view name{}; 39 | }; 40 | 41 | template auto to_tuple_with_names(const T& t); // TODO 42 | 43 | int main() { 44 | { 45 | struct empty { }; 46 | 47 | const auto & t = to_tuple_with_names(empty{}); 48 | 49 | expect(0 == std::tuple_size_v>); 50 | } 51 | 52 | { 53 | struct trade { 54 | std::int32_t price{42}; 55 | }; 56 | 57 | const auto & t = to_tuple_with_names(trade{}); 58 | 59 | expect(1 == std::tuple_size_v>); 60 | expect(42 == std::get<0>(t).value and "price" == std::get<0>(t).name); 61 | } 62 | 63 | { 64 | struct trade { 65 | std::int32_t price{42}; 66 | std::uint32_t quantity{1'000u}; 67 | }; 68 | 69 | const auto & t = to_tuple_with_names(trade{}); 70 | 71 | expect(2 == std::tuple_size_v>); 72 | expect(42 == std::get<0>(t).value and "price" == std::get<0>(t).name); 73 | expect(1'000u == std::get<1>(t).value and "quantity" == std::get<1>(t).name); 74 | }; 75 | } 76 | ``` 77 | 78 | > https://godbolt.org/z/sEhYvas7o 79 | 80 |

Solutions

81 | 82 | ```cpp 83 | template auto to_tuple_with_names(const T& t) { 84 | std::tuple...> result; 85 | result...[:] = { t...[:], @member_names(T) } ...; 86 | return result; 87 | } 88 | ``` 89 | 90 | > https://godbolt.org/z/cbdofa9MY 91 | 92 | ```cpp 93 | template [[nodiscard]] auto to_tuple_with_names(const T& t) { 94 | constexpr auto to_tuple_with_names = [](const TValues&... values) { 95 | return [&](const auto&... names) { 96 | return std::make_tuple(named{values, names}...); 97 | }; 98 | }; 99 | return to_tuple_with_names(t.@member_values()...)(@member_names(T)...); 100 | } 101 | ``` 102 | 103 | > https://godbolt.org/z/EzhbT9q8P 104 | 105 | ```cpp 106 | template auto to_tuple_with_names(const T& t) { 107 | std::tuple...> result; 108 | @meta for(int i = 0; i < @member_count(T); ++i) { 109 | std::get(result) = { @member_value(t, i), @member_name(T, i) }; 110 | } 111 | return result; 112 | } 113 | ``` 114 | 115 | ```cpp 116 | template auto to_tuple_with_names(const T& t) { 117 | return std::make_tuple(named<@member_types(T)>{@member_values(t), @member_names(T)}...); 118 | } 119 | ``` 120 | 121 | > https://godbolt.org/z/x9nWbWaEv 122 | 123 | ```cpp 124 | template 125 | [[nodiscard]] constexpr auto to_tuple_with_names(const T &t) { 126 | return std::make_tuple( 127 | named<@member_types(T)>{t.@member_values(), @member_names(T)}...); 128 | } 129 | ``` 130 | 131 | > https://godbolt.org/z/a1GK46e6n 132 | 133 | ```cpp 134 | template auto get_member( T const & obj) 135 | { 136 | return named{ obj.@member_value(I), @member_name(T, I) }; 137 | } 138 | template auto to_tuple_with_names(const T& t){ 139 | const int N = @member_count(T); 140 | return [&]( std::integer_sequence const & ) 141 | { 142 | return std::make_tuple(get_member(t)...); 143 | }(std::make_integer_sequence{} ); 144 | }; 145 | ``` 146 | 147 | > https://godbolt.org/z/8rch9eTaW 148 | -------------------------------------------------------------------------------- /tips/239.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know that Circle Meta-model allows to convert string to a type?** 4 | 5 | * http://wg21.link/p2062 6 | * https://github.com/seanbaxter/circle/blob/master/reflection/README.md 7 | 8 |

Example

9 | 10 | ```cpp 11 | int main() { 12 | @meta std::string type = "int"; 13 | @type_id(type) i = 42; // string -> type 14 | return i; // returns int = 4 15 | } 16 | ``` 17 | 18 | > https://godbolt.org/z/138zW9PvT 19 | 20 |

Puzzle

21 | 22 | * **Can you implement `strings_to_tuple` function which converts given strings into a `std::tuple` of them?** 23 | 24 | ```cpp 25 | template 26 | constexpr auto strings_to_tuple(Ts...); // TODO 27 | 28 | struct foo { 29 | int id{}; 30 | }; 31 | 32 | int main() { 33 | using namespace boost::ut; 34 | 35 | "string types to tuple"_test = [] { 36 | "empty"_test = [] { 37 | auto ts = strings_to_tuple(); 38 | expect(std::is_same_v, decltype(ts)>); 39 | }; 40 | 41 | "simple types"_test = [] { 42 | auto ts = strings_to_tuple([]{return "int";}, []{return "double";}); 43 | std::get<0>(ts) = 4; 44 | std::get<1>(ts) = 2.; 45 | expect(4_i == std::get<0>(ts) and 2._d == std::get<1>(ts)); 46 | }; 47 | 48 | "mix types"_test = [] { 49 | auto ts = strings_to_tuple([]{return "unsigned";}, []{return "foo";},[]{return "int";}); 50 | std::get<0>(ts) = 1.; 51 | std::get<1>(ts).id = 2; 52 | std::get<2>(ts) = 3; 53 | expect(1_u == std::get<0>(ts) and 2_i == std::get<1>(ts).id and 3_i == std::get<2>(ts)); 54 | }; 55 | }; 56 | } 57 | ``` 58 | 59 | > https://godbolt.org/z/PsEjEKfvd 60 | 61 |

Solutions

62 | 63 | ```cpp 64 | template 65 | constexpr auto strings_to_tuple(Ts...) { 66 | return std::tuple<(@type_id(Ts{}()))...>{}; 67 | } 68 | ``` 69 | 70 | > https://cpp-tip-of-the-week.godbolt.org/z/xzjP1fWbx 71 | 72 | ```cpp 73 | template 74 | constexpr auto strings_to_tuple(Ts...args ) 75 | { 76 | auto toTypedObj = []( auto arg ){ 77 | constexpr const char * tName = arg(); 78 | return @type_id(tName){}; 79 | }; 80 | return std::make_tuple(toTypedObj(args)...); 81 | } 82 | ``` 83 | 84 | > https://godbolt.org/z/hhcT76vTh 85 | 86 | ```cpp 87 | constexpr auto strings_to_tuple(Ts...) { 88 | return std::tuple<@type_id(Ts{}())...>{}; 89 | } 90 | ``` 91 | 92 | > https://godbolt.org/z/rej46v8a9 93 | 94 | ```cpp 95 | template 96 | constexpr auto strings_to_tuple(Ts ...) { 97 | return std::make_tuple(@type_id(Ts{}()){}...); 98 | } 99 | ``` 100 | 101 | > https://godbolt.org/z/4M3M7c5Mz 102 | -------------------------------------------------------------------------------- /tips/241.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know about different ways of accessing C-style arrays by index?** 4 | 5 | * http://eel.is/c++draft/dcl.array#def:array 6 | 7 |

Example

8 | 9 | ```cpp 10 | int main() { 11 | constexpr int array[] = {1, 2, 3}; 12 | assert(2[array] == array[2]); 13 | assert(*(array+1) == array[1]); 14 | } 15 | ``` 16 | 17 | > https://godbolt.org/z/5PnPzWvW8 18 | 19 |

Puzzle

20 | 21 | * **Can you implement `sum_n` which sums the first N numbers from the array using an altenative access syntax?** 22 | 23 | ```cpp 24 | template 25 | /*TODO*/ auto sum_n(auto); 26 | 27 | int main() { 28 | using namespace boost::ut; 29 | 30 | "sum_n"_test = [] { 31 | should("return 0 for empty array") = [] { 32 | constexpr int array[] = {}; 33 | expect(constant<0 == sum_n<0>(array)>); 34 | }; 35 | 36 | should("sum N first parameters") = [] { 37 | constexpr int array[] = {1, 2, 3}; 38 | expect(constant<0 == sum_n<0>(array)>); 39 | expect(constant<1 == sum_n<1>(array)>); 40 | expect(constant<3 == sum_n<2>(array)>); 41 | expect(constant<6 == sum_n<3>(array)>); 42 | }; 43 | }; 44 | } 45 | ``` 46 | 47 | > https://godbolt.org/z/5dMrTzGo7 48 | 49 |

Solutions

50 | 51 | ```cpp 52 | template constexpr auto sum_n(auto a) { 53 | std::decay_t sum{}; 54 | for (auto i = 0; i < N; ++i) { 55 | sum += i[a]; 56 | } 57 | return sum; 58 | } 59 | ``` 60 | 61 | > https://cpp-tip-of-the-week.godbolt.org/z/51fY56zdc 62 | 63 | ```cpp 64 | template 65 | constexpr auto sum_n(auto arr) { 66 | return [&] (std::integer_sequence) { 67 | return (0 + ... + Nth_of[arr]); 68 | }(std::make_integer_sequence{}); 69 | } 70 | ``` 71 | 72 | > https://godbolt.org/z/K9rcenjME 73 | 74 | ```cpp 75 | template 76 | [[nodiscard]] constexpr auto sum_n(const auto array) { 77 | return [array](std::index_sequence) { 78 | return (Is[array] + ... + 0); 79 | }(std::make_index_sequence{}); 80 | } 81 | ``` 82 | 83 | > https://godbolt.org/z/cjo8MovY8 84 | 85 | ```cpp 86 | template 87 | auto constexpr sum_n(auto arr) { 88 | double sum = 0; 89 | for(uint32_t i=0; i https://godbolt.org/z/Y6GxMMjx6 96 | 97 | ```cpp 98 | template 99 | auto constexpr get_i(auto const & array) 100 | { 101 | if constexpr (I % 3 == 0 ) 102 | return array[I]; 103 | else if constexpr (I % 3 == 1) 104 | return I[array]; 105 | else 106 | return *(array + I ); 107 | } 108 | template 109 | auto constexpr sum_n(auto const & array) 110 | { 111 | return [&]( std::index_sequence const & ) 112 | { 113 | return (get_i(array) + ... + 0 ); 114 | }(std::make_index_sequence()); 115 | } 116 | ``` 117 | 118 | > https://godbolt.org/z/cx4nfGhvz 119 | 120 | ```cpp 121 | template 122 | constexpr auto sum_n(const auto& array) { 123 | return [&](std::index_sequence) { 124 | return (0 + ... + Ith_index_of[array]); 125 | }(std::make_index_sequence{}); 126 | } 127 | ``` 128 | 129 | > https://godbolt.org/z/d3Gcaq7Pn 130 | 131 | ```cpp 132 | template 133 | [[nodiscard]] constexpr auto sum_n(const auto array) { 134 | return [array](std::index_sequence) { 135 | return (Is[array] + ... + 0); 136 | }(std::make_index_sequence{}); 137 | } 138 | ``` 139 | 140 | > https://godbolt.org/z/3nf6aKM4r 141 | -------------------------------------------------------------------------------- /tips/247.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know that `Deducing this` proposal has been voted out into C++23?** 4 | 5 | * http://wg21.link/p0847 6 | 7 |

Example

8 | 9 | ```cpp 10 | struct foo { 11 | auto bar(bool b) { return b; } 12 | 13 | // deducing this 14 | auto baz(this foo const & self, bool b) { return not b; } 15 | }; 16 | 17 | static_assert(foo{}.bar(true)); 18 | static_assert(not foo{}.baz(true)); 19 | 20 | static_assert(not foo{}.bar(false)); 21 | static_assert(foo{}.baz(false)); 22 | ``` 23 | 24 | > https://circle.godbolt.org/z/TMc63G5Tf 25 | 26 |

Puzzle

27 | 28 | * **Can you implement sum which adds args... by using a recursive lambda with deduced this?** 29 | 30 | ```cpp 31 | constexpr auto sum = [](...); // TODO 32 | 33 | static_assert(0 == sum()); 34 | static_assert(1 == sum(1)); 35 | static_assert(3 == sum(1, 2)); 36 | static_assert(6 == sum(1, 2, 3)); 37 | static_assert(6 == sum(2, 3, 1)); 38 | ``` 39 | 40 | > https://circle.godbolt.org/z/j8a7oc8nT 41 | 42 |

Solutions

43 | 44 | ```cpp 45 | constexpr auto sum = [](this const auto& self, auto... args) { 46 | if constexpr (sizeof...(args) == 0) { 47 | return 0; 48 | } else { 49 | return [&](auto arg, auto... args) { return arg + self(args...); }(args...); 50 | } 51 | }; 52 | ``` 53 | 54 | > https://circle.godbolt.org/z/Gf7PdP64G 55 | 56 | ```cpp 57 | constexpr auto sum = [](this auto self, auto ... args) 58 | { 59 | auto peelArg = [&](auto arg, auto ...args) { 60 | return arg + self(args...); 61 | }; 62 | 63 | if constexpr (sizeof...(args) == 0) 64 | return 0; 65 | else 66 | return peelArg(args...); 67 | }; 68 | 69 | ``` 70 | 71 | > https://circle.godbolt.org/z/vnrTEnGcr 72 | 73 | ```cpp 74 | constexpr auto sum = [](this const auto& self, const auto&... args) { 75 | if constexpr(sizeof...(args) == 0) { 76 | return 0; 77 | } else { 78 | return [self](const auto& first_arg, const auto&... remaining_args) { 79 | return first_arg + self(remaining_args...); 80 | }(args...); 81 | } 82 | }; 83 | 84 | ``` 85 | 86 | > https://circle.godbolt.org/z/7cz5WbTd3 87 | 88 | ```cpp 89 | constexpr auto sum = [](this auto & self, auto ... Is ){ 90 | if constexpr ( (sizeof ...( Is) ) == 0 ) 91 | return 0; 92 | else 93 | return [&]( auto I , auto ... Iss ) { return I + self.operator()( Iss ... ) ; }( Is...); 94 | }; 95 | ``` 96 | 97 | > https://circle.godbolt.org/z/7d8acTPWE 98 | 99 | ```cpp 100 | constexpr auto sum = [] (this const auto &self, auto... args) { 101 | if constexpr (sizeof...(args) >= 1) { 102 | return [=] (auto first, auto... rest) { 103 | return first + self(rest...); 104 | }(args...); 105 | } 106 | 107 | return 0; 108 | }; 109 | ``` 110 | 111 | > https://circle.godbolt.org/z/c4Pjb51cc 112 | 113 | ```cpp 114 | constexpr auto sum = [](this const auto& self, auto... args) { 115 | if constexpr (sizeof...(args) > 0) { 116 | return [self](const auto head, const auto... tail) { 117 | return head + self(tail...); 118 | }(args...); 119 | } 120 | return 0; 121 | }; 122 | ``` 123 | 124 | > https://circle.godbolt.org/z/PzzEKs14q 125 | 126 | ```cpp 127 | constexpr auto sum = [](this auto const &self, auto ...args){ 128 | if constexpr ( sizeof...(args) == 0 ){ 129 | return 0; 130 | } else { 131 | return [=](const auto arg, auto ... rest_of_args){ 132 | return arg + self(rest_of_args...); 133 | }(args...); 134 | } 135 | }; 136 | ``` 137 | 138 | > https://circle.godbolt.org/z/zas389zWT 139 | -------------------------------------------------------------------------------- /tips/249.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know that C++23 allows extended init-statement with alias-declaration in the for loop?** 4 | 5 | * http://wg21.link/p2360 6 | 7 |

Example

8 | 9 | ```cpp 10 | #include 11 | #include 12 | 13 | int main() { 14 | for (using T = int; T e : {1, 2}) { 15 | std::cout << e; // prints 1,2 16 | } 17 | 18 | for (struct T { int x; int y; }; T e : {T{1,2}, T{3,4}}) { 19 | std::cout << "{" << e.x << ',' << e.y << '}'; // prints {1,2}{3,4} 20 | } 21 | } 22 | ``` 23 | 24 | > https://godbolt.org/z/Y3nvPafec 25 | 26 |

Puzzle

27 | 28 | * **Can you implement an init-statement for the for loop which will add an anonymous struct {x,y} and an alias declaration T to it?** 29 | 30 | ```cpp 31 | auto print(std::ostream& os, auto... args) -> std::ostream& { 32 | for (/*TODO*/ : {T{args.first, args.second}...}) { 33 | os << '{' << e.x << ',' << e.y << '}'; 34 | } 35 | return os; 36 | } 37 | 38 | int main() { 39 | using namespace boost::ut; 40 | 41 | "print"_test = [] { 42 | "empty"_test = [] { 43 | std::stringstream str{}; 44 | print(str); 45 | expect(std::string_view{""} == str.str()); 46 | }; 47 | 48 | "single pair"_test = [] { 49 | std::stringstream str{}; 50 | print(str, std::pair{1,2}); 51 | expect(std::string_view{"{1,2}"} == str.str()); 52 | }; 53 | 54 | "multiple pairs"_test = [] { 55 | std::stringstream str{}; 56 | print(str, std::pair{1,3}, std::pair{2, 4}); 57 | expect(std::string_view{"{1,3}{2,4}"} == str.str()); 58 | }; 59 | }; 60 | } 61 | ``` 62 | 63 | > https://godbolt.org/z/zjWGhnGnf 64 | 65 |

Solutions

66 | 67 | ```cpp 68 | auto print(std::ostream& os, auto... args) -> std::ostream& { 69 | for (using T = struct { int x; int y; }; const T& e : std::initializer_list{T{args.first, args.second}...}) { 70 | os << '{' << e.x << ',' << e.y << '}'; 71 | } 72 | return os; 73 | } 74 | ``` 75 | 76 | > https://godbolt.org/z/xzd398z3n 77 | 78 | ```cpp 79 | auto print(std::ostream& os) -> std::ostream& { return os; } 80 | auto print(std::ostream& os, auto... args) -> std::ostream& { 81 | for (using T = struct { int x; int y; }; const T& e : {T{args.first, args.second}...}) { 82 | os << '{' << e.x << ',' << e.y << '}'; 83 | } 84 | return os; 85 | } 86 | ``` 87 | 88 | > https://godbolt.org/z/cehWYKz5a 89 | 90 | ```cpp 91 | auto print(std::ostream& os, auto... args) -> std::ostream& { 92 | for (using T = struct {int x; int y;}; const T & e : std::vector{T{args.first, args.second}...}) { 93 | os << '{' << e.x << ',' << e.y << '}'; 94 | } 95 | return os; 96 | } 97 | ``` 98 | 99 | > https://godbolt.org/z/3WaPGeG5b 100 | 101 | ```cpp 102 | auto print(std::ostream& os, auto... args) -> std::ostream& { 103 | for (struct T { int x; int y; }; const auto &e : std::initializer_list{{args.first, args.second}...}) { 104 | os << '{' << e.x << ',' << e.y << '}'; 105 | } 106 | return os; 107 | } 108 | ``` 109 | 110 | > https://godbolt.org/z/YdYsfTW1a 111 | 112 | ```cpp 113 | auto print(std::ostream& os, auto... args) -> std::ostream& { 114 | if constexpr (sizeof...(args) > 0) { 115 | for (using T = struct { int x; int y; }; auto const& e : {T{args.first, args.second}...}) { 116 | os << '{' << e.x << ',' << e.y << '}'; 117 | } 118 | } 119 | return os; 120 | } 121 | ``` 122 | 123 | > https://godbolt.org/z/n9reaY7hE 124 | 125 | ```cpp 126 | auto print(std::ostream& os, auto... args) -> std::ostream& { 127 | for (using T = struct { int x; int y;}; T const & e : std::array{T{args.first, args.second}...}) { 128 | os << '{' << e.x << ',' << e.y << '}'; 129 | } 130 | return os; 131 | } 132 | ``` 133 | 134 | > https://godbolt.org/z/aKT6ocr6c 135 | -------------------------------------------------------------------------------- /tips/253.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know that C++20 extends support for data time utilities?** 4 | 5 | * https://eel.is/c++draft/#time 6 | 7 |

Example

8 | 9 | ```cpp 10 | constexpr auto thanksgiving = November / 25 / 2021y; 11 | 12 | static_assert(year(2021) == thanksgiving.year()); 13 | static_assert(month(11) == thanksgiving.month()); 14 | static_assert(day(25) == thanksgiving.day()); 15 | ``` 16 | 17 | > https://godbolt.org/z/eda4d35xT 18 | 19 |

Puzzle

20 | 21 | * **Can you apply std::chrono data time utilities to verify Thanksgiving dates**? 22 | 23 | ```cpp 24 | constexpr auto thanksgiving = November / 25 / 2021y; 25 | constexpr auto cpptipday = 21d / 11 / 2021; 26 | 27 | static_assert( /*is thanksgivng a valid date?*/); 28 | static_assert(Thursday == /*what day is thanksgiving?*/); 29 | static_assert(days(4) == /*how many days to thanksgivnig from cpptipday?*/); 30 | static_assert(25d / November/ 2022 == /*when will be thankgiving next year?*/); 31 | ``` 32 | 33 | > https://godbolt.org/z/6h755jqPE 34 | 35 |

Solutions

36 | 37 | ```cpp 38 | static_assert(thanksgiving.ok() /*is thanksgiving a valid date?*/); 39 | static_assert(Thursday == weekday{sys_days{thanksgiving}} /*what day is thanksgiving?*/); 40 | static_assert(days(4) == sys_days{thanksgiving} - sys_days{cpptipday} /*how many days to thanksgiving from cpptipday?*/); 41 | static_assert(24d / November/ 2022 == sys_days{Thursday[4] / November / 2022} /*when will be thanksgiving next year?*/); 42 | ``` 43 | 44 | > https://jonathan.godbolt.org/z/G6T4zEKn1 45 | 46 | ```cpp 47 | static_assert(thanksgiving.ok()); 48 | static_assert(Thursday == weekday{sys_days(thanksgiving)}); 49 | static_assert(days(4) == thanksgiving.day() - cpptipday.day()); 50 | static_assert(25d / November/ 2022 == thanksgiving + years(1)); 51 | ``` 52 | 53 | > https://godbolt.org/z/e37KPxGbv 54 | 55 | 56 | ```cpp 57 | static_assert(thanksgiving.ok() /*is thanksgivng a valid date?*/); 58 | static_assert(Thursday == weekday{thanksgiving} /*what day is thanksgiving?*/); 59 | static_assert(days(4) == thanksgiving.day() - cpptipday.day() /*how many days to thanksgiving from cpptipday?*/); 60 | static_assert(24d / November/ 2022 == sys_days{2022y / November/ Thursday[4]} /*when will be thankgiving next year?*/); 61 | ``` 62 | 63 | > https://godbolt.org/z/qKcE4x373 64 | -------------------------------------------------------------------------------- /tips/258.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know that static reflection can be used to invoke functions with named parameters?** 4 | 5 | * https://wg21.link/P0385 6 | 7 |

Example

8 | 9 | ```cpp 10 | struct foo { 11 | auto api(int i) -> void { std::cout << i; } 12 | }; 13 | 14 | auto main() -> int { 15 | namespace meta = std::experimental::reflect; 16 | foo f{}; 17 | (f.*meta::get_pointer_v>>)(42); // prints 42 18 | } 19 | ``` 20 | 21 | > https://godbolt.org/z/sMeeo981v 22 | 23 |

Puzzle

24 | 25 | * **Can you implement invoke which calls given by name function from reflected struct with given named parameters?** 26 | 27 | ```cpp 28 | template /*TODO*/ invoke{}; 29 | 30 | struct foo { 31 | auto bar(int value) { bar_called_with = value; } 32 | auto baz(int i, double d) { baz_called_with = {i, d}; } 33 | 34 | std::optional bar_called_with{}; 35 | std::optional> baz_called_with{}; 36 | }; 37 | 38 | int main() { 39 | using namespace boost::ut; 40 | 41 | "invoke"_test = [] { 42 | should("support single value") = [] { 43 | foo f{}; 44 | invoke<"bar">(f, "value"_arg = 42); 45 | expect(not f.baz_called_with and f.bar_called_with and *f.bar_called_with == 42_i); 46 | }; 47 | 48 | should("support diff order") = [] { 49 | foo f{}; 50 | invoke<"baz">(f, "i"_arg = 4, "d"_arg = 2.0); 51 | expect(not f.bar_called_with and f.baz_called_with and *f.baz_called_with == std::pair{4, 2.}); 52 | }; 53 | 54 | should("support diff order") = [] { 55 | foo f{}; 56 | invoke<"baz">(f, "d"_arg = 0., "i"_arg = 42); 57 | expect(not f.bar_called_with and f.baz_called_with and *f.baz_called_with == std::pair{42, .0}); 58 | }; 59 | }; 60 | } 61 | ``` 62 | 63 | > https://godbolt.org/z/zTnEeaTxf 64 | 65 |

Solutions

66 | 67 | ```cpp 68 | template decltype(auto) invoke(T& t, Args&&...args) { 69 | const auto get_arg = [&] { 70 | return ([&] -> meta::get_reflected_type_t> { 71 | if constexpr(std::string_view{meta::get_name_v

} == Args::name) { 72 | return args.value; 73 | } else { 74 | return {}; 75 | }}() + ...); // exploiting + 76 | }; 77 | 78 | const auto call = [&] -> bool { 79 | if constexpr (std::string_view{meta::get_name_v} == Name) { 80 | const auto f = meta::get_pointer_v; 81 | using params_t = meta::get_parameters_t; 82 | [&] (TParams, std::index_sequence) { 83 | (t.*f)(get_arg.template operator()>()...); 84 | }(params_t{}, std::make_index_sequence>{}); 85 | return true; 86 | } else { 87 | return false; 88 | } 89 | }; 90 | 91 | using type = meta::get_aliased_t; 92 | using member_functions_t = meta::get_member_functions_t; 93 | std::apply([&] (Fs...) { 94 | (call.template operator()() or ...); 95 | }, meta::unpack_sequence_t{}); 96 | } 97 | ``` 98 | 99 | > https://godbolt.org/z/Ka3xYYd7a 100 | -------------------------------------------------------------------------------- /tips/260.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know that C++23 added std::move_only_function?** 4 | 5 | * https://wg21.link/P0288 6 | 7 |

Example

8 | 9 | ```cpp 10 | #include 11 | 12 | int main() { 13 | { 14 | std::function f{[]{return 42; }}; 15 | auto copy = f; // okay 16 | auto value = f(); 17 | } 18 | 19 | { 20 | std::move_only_function f{[] {return 42; }}; 21 | auto copy = f; // error, call to deleted copy constructor 22 | auto value = f(); // undefined behaviour, dandling reference 23 | } 24 | } 25 | ``` 26 | 27 | > https://godbolt.org/z/KWTx4nd3n 28 | 29 |

Puzzle

30 | 31 | * **Can you comment out and fill `???` with appropirate function type (std::function or std::move_only_function) when applicable?** 32 | 33 | ```cpp 34 | int main() { 35 | { 36 | auto f = [] { return 42; }; 37 | // TODO 38 | // ??? f1 = f; 39 | // ??? f2 = std::move(f); 40 | } 41 | 42 | { 43 | auto value = 42; 44 | auto f = [v = std::move(value)] { return v; }; 45 | // TODO 46 | // ??? f1 = f; 47 | // ??? f2 = std::move(f); 48 | } 49 | 50 | { 51 | auto f = [u = std::make_unique(42)] { return *u; }; 52 | // TODO 53 | // ??? f1 = f; 54 | // ??? f2 = std::move(f); 55 | } 56 | } 57 | ``` 58 | 59 | > https://godbolt.org/z/hK4EGvd1q 60 | 61 |

Solutions

62 | -------------------------------------------------------------------------------- /tips/261.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know that C++23 added Monadic operations for std::optional?** 4 | 5 | * https://wg21.link/P0798 6 | 7 |

Example

8 | 9 | ```cpp 10 | int main() { 11 | auto opt = std::optional{42}; 12 | opt.and_then([](auto o)->std::optional{ std::cout << o;; return std::nullopt; });// prints 42 13 | } 14 | ``` 15 | 16 | > https://godbolt.org/z/aeKWEa63b 17 | 18 |

Puzzle

19 | 20 | * **Can you refactor `execute` routine with monadic Monadic operations?** 21 | 22 | ```cpp 23 | struct market_data{}; 24 | struct trade{}; 25 | struct order{}; 26 | struct order_with_id{}; 27 | 28 | std::optional execute(auto& ts, const market_data& md) { 29 | auto trade = ts.parse(md); 30 | if (not trade) { 31 | return std::nullopt; 32 | } 33 | 34 | auto order = ts.model(*trade); 35 | if (not order) { 36 | return std::nullopt; 37 | } 38 | 39 | if (auto order_with_id = ts.map(*order)) { 40 | return order_with_id; 41 | } else { 42 | return std::nullopt; 43 | } 44 | } 45 | 46 | int main(){ 47 | using namespace boost::ut; 48 | 49 | should("produce bail out on market data") = [] { 50 | struct { 51 | auto parse(const market_data&) -> std::optional { return {}; } 52 | auto model(const trade&) -> std::optional { return {{}}; } 53 | auto map(const order&) -> std::optional { return {{}}; } 54 | } fake_ts; 55 | 56 | expect(not execute(fake_ts, {}).has_value()); 57 | }; 58 | 59 | should("produce bail out on model") = [] { 60 | struct { 61 | auto parse(const market_data&) -> std::optional { return {{}}; } 62 | auto model(const trade&) -> std::optional { return {}; } 63 | auto map(const order&) -> std::optional { return {{}}; } 64 | } fake_ts; 65 | 66 | expect(not execute(fake_ts, {}).has_value()); 67 | }; 68 | 69 | should("produce bail out on map") = [] { 70 | struct { 71 | auto parse(const market_data&) -> std::optional { return {{}}; } 72 | auto model(const trade&) -> std::optional { return {{}}; } 73 | auto map(const order&) -> std::optional { return {}; } 74 | } fake_ts; 75 | 76 | expect(not execute(fake_ts, {}).has_value()); 77 | }; 78 | 79 | should("produce an order") = [] { 80 | struct { 81 | auto parse(const market_data&) -> std::optional { return {{}}; } 82 | auto model(const trade&) -> std::optional { return {{}}; } 83 | auto map(const order&) -> std::optional { return {{}}; } 84 | } fake_ts; 85 | 86 | expect(execute(fake_ts, {}).has_value()); 87 | }; 88 | } 89 | ``` 90 | 91 | > https://godbolt.org/z/oG4qMPxEe 92 | 93 |

Solutions

94 | 95 | ```cpp 96 | std::optional execute(auto& ts, const market_data& md) { 97 | return ts.parse(md) 98 | .and_then([&](const trade& t) { return ts.model(t); }) 99 | .and_then([&](const order& o) { return ts.map(o); }); 100 | } 101 | ``` 102 | 103 | > https://godbolt.org/z/z36z3f9cd 104 | 105 | ```cpp 106 | std::optional execute(auto& ts, const market_data& md) { 107 | auto model = [&ts](const auto &t){ return ts.model(t); }; 108 | auto map = [&ts](const auto &o){ return ts.map(o); }; 109 | 110 | return ts.parse(md) 111 | .and_then(model) 112 | .and_then(map); 113 | } 114 | ``` 115 | 116 | > https://godbolt.org/z/se6MYh11q 117 | 118 | ```cpp 119 | decltype(auto) operator|(auto&& x, auto&& p) { return x.and_then(p); } 120 | 121 | std::optional execute(auto& ts, const market_data& md) { 122 | return ts.parse(md) | [&ts](auto o) { return ts.model(o); } | 123 | [&ts](auto o) { return ts.map(o); }; 124 | } 125 | ``` 126 | 127 | > https://godbolt.org/z/GEf5j6zej 128 | -------------------------------------------------------------------------------- /tips/264.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know that C++20 added `__VA_OPT__` for comma omission and comma deletion?** 4 | 5 | * https://wg21.link/P0306 6 | 7 |

Example

8 | 9 | ```cpp 10 | #define VARIADIC(...) __VA_OPT__(__LINE__) 11 | 12 | VARIADIC() // `empty` 13 | VARIADIC(a) // `line` 4 14 | VARIADIC(a, b) // `line` 5 15 | ``` 16 | 17 | > https://godbolt.org/z/rsj9ax7xY 18 | 19 |

Puzzle

20 | 21 | * **Can you implement LOG1/LOG2 macros which will return formatted string and will apply `__VA_OPT__`?** 22 | 23 | ```cpp 24 | #define LOG1($fmt, ...) // TODO 25 | #define LOG2(...) // TODO 26 | 27 | int main() { 28 | using namespace boost::ut; 29 | using std::string_literals::operator""s; 30 | 31 | expect(""s == LOG1("")); 32 | expect("42"s == LOG1("42")); 33 | expect("4"s == LOG1("%d", 4)); 34 | 35 | expect(""s == LOG2("")); 36 | expect("42"s == LOG2("42")); 37 | expect("4"s == LOG2("%d", 4)); 38 | } 39 | ``` 40 | 41 | > https://godbolt.org/z/sPTqoEdMG 42 | 43 |

Solutions

44 | 45 | ```cpp 46 | #define LOG1($fmt, ...) fmt::sprintf($fmt __VA_OPT__(,) __VA_ARGS__) 47 | #define LOG2(...) __VA_OPT__(fmt::sprintf(__VA_ARGS__)) 48 | ``` 49 | 50 | > https://godbolt.org/z/5TM7WsMfx 51 | 52 | ```cpp 53 | #define LOG1($fmt, ...) fmt::sprintf($fmt __VA_OPT__(,) __VA_ARGS__) 54 | #define LOG2(...) fmt::sprintf(__VA_ARGS__) 55 | ``` 56 | 57 | > https://godbolt.org/z/135j8s5PP 58 | -------------------------------------------------------------------------------- /tips/265.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know that C++23 added Attributes on Lambda-Expressions?** 4 | 5 | * https://wg21.link/P2173 6 | 7 |

Example

8 | 9 | ```cpp 10 | constexpr auto foo = [] [[deprecated]] { }; 11 | 12 | int main() { 13 | foo(); // operator() is deprecated 14 | } 15 | ``` 16 | 17 | > https://godbolt.org/z/MaeTnG9eb 18 | 19 |

Puzzle

20 | 21 | * **Can you implement variable template lambda expression `foo` which is marked nodiscard for integral types?** 22 | 23 | ```cpp 24 | //TODO foo 25 | 26 | int main() { 27 | using namespace boost::ut; 28 | 29 | "should be callable for non integral types"_test = [] { 30 | foo(); 31 | foo(); 32 | }; 33 | 34 | "should verify the result for integral types"_test = [] { 35 | expect(0_c == foo()); 36 | expect(not foo()); 37 | expect(0_s == foo()); 38 | expect(0_i == foo()); 39 | }; 40 | 41 | "should ignore the result for integral types"_test = [] { 42 | /*TODO*/ foo(); 43 | /*TODO*/ foo(); 44 | /*TODO*/ foo(); 45 | /*TODO*/ foo(); 46 | }; 47 | } 48 | ``` 49 | 50 | > https://godbolt.org/z/fKfjsbWoh 51 | 52 |

Solutions

53 | -------------------------------------------------------------------------------- /tips/266.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know wrapping an unqualified function name in parentheses suppresses argument-dependent lookup?** 4 | 5 | * http://eel.is/c++draft/basic.lookup#basic.lookup.argdep-1 6 | 7 |

Example

8 | 9 | ```cpp 10 | namespace adl { 11 | struct foo {}; 12 | void bar(foo) {} 13 | } 14 | 15 | int main() { 16 | adl::foo foo; 17 | bar(foo); // OK, ADL 18 | (bar)(foo); // error: no ADL 19 | } 20 | ``` 21 | 22 | > https://godbolt.org/z/ba7Pvqa75 23 | 24 |

Puzzle

25 | 26 | * **Can you implement function bar which returns { 0: if it's not ADL call, 42: if it's a qualified call; ambigious error: if it's an ADL call }?** 27 | 28 | ```cpp 29 | #include 30 | 31 | //TODO 32 | 33 | int main() { 34 | static_assert([](auto foo) { return requires { adl::bar(foo); }; }(adl::foo{})); 35 | assert(42 == adl::bar(adl::foo{})); 36 | 37 | static_assert([](auto foo) { return requires { ::bar(foo); }; }(adl::foo{})); 38 | assert(0 == ::bar(adl::foo{})); 39 | 40 | static_assert([](auto foo) { return requires { (bar)(foo); }; }(adl::foo{})); 41 | assert(0 == (bar)(adl::foo{})); 42 | 43 | static_assert(not [](auto foo) { return requires { bar(foo); }; }(adl::foo{})); 44 | } 45 | ``` 46 | 47 | > https://godbolt.org/z/cxhxW9n8Y 48 | 49 |

Solutions

50 | 51 | ```cpp 52 | namespace adl { 53 | struct foo {}; 54 | constexpr auto bar(foo) -> int { return 42; } 55 | } 56 | 57 | constexpr auto bar(adl::foo) -> int { return 0; } 58 | ``` 59 | 60 | > https://godbolt.org/z/MPTfK7sKq 61 | 62 | ```cpp 63 | namespace adl { 64 | 65 | struct foo {}; 66 | [[nodiscard]] constexpr auto bar(const foo) noexcept -> int { return 42; } 67 | 68 | } // namespace adl 69 | 70 | [[nodiscard]] constexpr auto bar(const adl::foo) noexcept -> int { return 0; } 71 | ``` 72 | 73 | > https://godbolt.org/z/73YbYorj3 74 | -------------------------------------------------------------------------------- /tips/277.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know that C++17 structured bindings support to custom classes can be added?** 4 | 5 | * https://wg21.link/P1061 6 | 7 |

Example

8 | 9 | ```cpp 10 | struct foo { 11 | int i{}; 12 | std::string s{}; 13 | }; 14 | 15 | template 16 | const auto& get(const foo& f) { 17 | if constexpr (N == 0) { 18 | return f.i; 19 | } else if constexpr (N == 1) { 20 | return f.s; 21 | } 22 | } 23 | 24 | namespace std { 25 | template <> 26 | struct tuple_size<::foo> : integral_constant {}; 27 | 28 | template 29 | struct tuple_element { 30 | using type = decltype(get(std::declval<::foo&>())); 31 | }; 32 | } // namespace std 33 | 34 | int main() { 35 | auto [i, s] = foo{.i = 42, .s = "str"}; 36 | assert(42 == i); 37 | assert("str" == s); 38 | } 39 | ``` 40 | 41 | > https://godbolt.org/z/n66GMfWao 42 | 43 |

Puzzle

44 | 45 | * **Can you add structured bindings support to std::index_sequence?** 46 | 47 | ```cpp 48 | namespace std { 49 | // TODO 50 | } 51 | 52 | int main() { 53 | { 54 | auto [... Is] = std::make_index_sequence<0>{}; 55 | static_assert(sizeof...(Is) == 0); 56 | } 57 | { 58 | auto [... Is] = std::make_index_sequence<3>{}; 59 | static_assert(sizeof...(Is) == 3); 60 | static_assert( 61 | typeid(std::tuple{std::integral_constant{}, 62 | std::integral_constant{}, 63 | std::integral_constant{}}) == 64 | typeid(std::tuple{Is...})); 65 | } 66 | { 67 | auto [... Is] = std::make_index_sequence<42>{}; 68 | static_assert(sizeof...(Is) == 42); 69 | } 70 | } 71 | ``` 72 | 73 | > https://godbolt.org/z/qavcThr9K 74 | 75 |

Solutions

76 | 77 | ```cpp 78 | namespace std { 79 | 80 | template 81 | using index_constant = std::integral_constant; 82 | 83 | template 84 | auto get(index_sequence) { 85 | return index_constant{}; 86 | } 87 | 88 | template 89 | struct tuple_size> : index_constant {}; 90 | 91 | template 92 | struct tuple_element> { 93 | using type = index_constant; 94 | }; 95 | 96 | } // namespace std 97 | ``` 98 | 99 | > https://godbolt.org/z/466j7snqK 100 | 101 | ```cpp 102 | namespace std { 103 | 104 | template 105 | auto constexpr get(const index_sequence&) { 106 | return get(tuple{integral_constant{}...}); 107 | }; 108 | 109 | template 110 | struct tuple_size> : integral_constant {}; 111 | 112 | template 113 | struct tuple_element> { 114 | using type = decltype(get(tuple{integral_constant{}...})); 115 | }; 116 | } 117 | ``` 118 | 119 | > https://godbolt.org/z/6fb7e89fK 120 | 121 | ```cpp 122 | namespace stdext { 123 | 124 | template 125 | struct index_sequence : std::index_sequence { 126 | template 127 | auto get() const -> std::tuple_element_t { 128 | return {}; 129 | } 130 | }; 131 | 132 | namespace detail { 133 | 134 | template > 135 | struct make_index_sequence; 136 | 137 | template 138 | struct make_index_sequence> { 139 | using type = index_sequence; 140 | }; 141 | 142 | } // namespace detail 143 | 144 | template 145 | using make_index_sequence = typename detail::make_index_sequence::type; 146 | 147 | } // namespace stdext 148 | ``` 149 | 150 | > https://godbolt.org/z/jeY9qoYG4 151 | -------------------------------------------------------------------------------- /tips/278.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know that C++23 added Literal Suffix for (signed) size_t?** 4 | 5 | * https://wg21.link/p0330 6 | 7 |

Example

8 | 9 | ```cpp 10 | static_assert(-42 == -42z); 11 | static_assert(-42 == -42Z); 12 | static_assert(42 == 42uz); 13 | static_assert(42 == 42uZ); 14 | static_assert(42 == 42Uz); 15 | static_assert(42 == 42ZU); 16 | static_assert(42 == 42Zu); 17 | 18 | static_assert(std::is_same_v); 19 | ``` 20 | 21 | > https://godbolt.org/z/Wxe9Kfze3 22 | 23 |

Puzzle

24 | 25 | * **Can you make the following snippets compatible with 32/64 bit arch?** 26 | 27 | ```cpp 28 | int main() { 29 | std::vector v{1, 2, 3}; 30 | 31 | for (auto i = 0; i < v.size(); ++i) { 32 | } 33 | 34 | for (auto i = 0, s = std::size(v); i < s; ++i) { 35 | } 36 | 37 | std::max(0, std::ssize(v)); 38 | std::min(0, std::size(v)); 39 | std::max(0, std::min(0, v.size())); 40 | } 41 | ``` 42 | 43 | > https://godbolt.org/z/EEdfz5451 44 | 45 |

Solutions

46 | 47 | ```cpp 48 | for (auto i = 0uz; i < v.size(); ++i) { 49 | } 50 | 51 | for (auto i = 0uz, s = std::size(v); i < s; ++i) { 52 | } 53 | 54 | std::max(0z, std::ssize(v)); 55 | std::min(0uz, std::size(v)); 56 | std::max(0uz, std::min(0uz, v.size())); 57 | ``` 58 | 59 | > https://godbolt.org/z/dfTbhx1r7 60 | 61 | ```cpp 62 | #include // Change vector to array for elision of generated code. :) 63 | 64 | int main() { 65 | std::array v{1, 2, 3}; 66 | 67 | for (auto i = 0uz; i < v.size(); ++i) { 68 | } 69 | 70 | for (auto i = 0uz, s = std::size(v); i < s; ++i) { 71 | } 72 | 73 | std::max(0z, std::ssize(v)); 74 | std::min(0uz, std::size(v)); 75 | std::max(0uz, std::min(0uz, v.size())); 76 | } 77 | ``` 78 | 79 | > https://godbolt.org/z/GhdW1Ebo8 80 | 81 | ```cpp 82 | int main() { 83 | std::vector v{1, 2, 3}; 84 | 85 | for (auto i = 0uz; i < v.size(); ++i) { 86 | } 87 | 88 | for (auto i = 0uz, s = std::size(v); i < s; ++i) { 89 | } 90 | 91 | std::max(0z, std::ssize(v)); 92 | std::min(0uz, std::size(v)); 93 | std::max(0uz, std::min(0uz, v.size())); 94 | } 95 | ``` 96 | 97 | > https://godbolt.org/z/KeKKPG7cv 98 | 99 | ```cpp 100 | int main() { 101 | std::vector v{1, 2, 3}; 102 | 103 | for (auto i = 0UZ; i < v.size(); ++i) { 104 | } 105 | 106 | for (auto i = 0UZ, s = std::size(v); i < s; ++i) { 107 | } 108 | 109 | std::max(0Z, std::ssize(v)); 110 | std::min(0UZ, std::size(v)); 111 | std::max(0UZ, std::min(0UZ, v.size())); 112 | } 113 | ``` 114 | 115 | > https://godbolt.org/z/xxqzE1cee 116 | -------------------------------------------------------------------------------- /tips/279.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * Did you know that C++20 made `std::string` constexpr? 4 | 5 | * https://wg21.link/P0980R1 6 | 7 |

Example

8 | 9 | ```cpp 10 | #include 11 | 12 | constexpr auto foo() { 13 | return std::string{"bar"}; 14 | } 15 | 16 | static_assert("bar" == foo()); 17 | ``` 18 | 19 | > https://godbolt.org/z/P9qW6jYav 20 | 21 |

Puzzle

22 | 23 | * **Can you implement `concat` which concatenates given strings?** 24 | 25 | ```cpp 26 | [[nodiscard]] constexpr auto concat(auto... args); // TODO 27 | 28 | static_assert(""s == concat()); 29 | static_assert("a"s == concat([]{return "a"s;})); 30 | static_assert("ab"s == concat([]{return "a"s;}, []{return "b"s;})); 31 | static_assert("abc"s == concat([]{return "a"s;}, []{return "b"s;}, []{return "c"s;})); 32 | ``` 33 | 34 | > https://godbolt.org/z/6MaMqrKzE 35 | 36 |

Solutions

37 | 38 | ```cpp 39 | [[nodiscard]] constexpr auto concat(auto... args) { 40 | return ( ""s + ... + args() ); 41 | } 42 | ``` 43 | 44 | > https://cpp_tip_of_the_week.godbolt.org/z/3f7fh4Yd5 45 | 46 | ```cpp 47 | [[nodiscard]] constexpr auto concat(auto... args) { 48 | std::string result{}; 49 | (result.append(args()), ...); 50 | return result; 51 | } 52 | ``` 53 | 54 | > https://godbolt.org/z/xh8oancaf 55 | 56 | ```cpp 57 | [[nodiscard]] constexpr auto concat(auto... args) { 58 | return (args() + ... + std::string{}); 59 | } 60 | ``` 61 | 62 | > https://godbolt.org/z/rjPz7qnn8 63 | 64 | ```cpp 65 | [[nodiscard]] constexpr auto concat(auto... args){ 66 | if constexpr(sizeof...(args) > 0){ 67 | return std::string{(args() + ...)}; 68 | } 69 | return std::string{}; 70 | } 71 | ``` 72 | 73 | > https://godbolt.org/z/c4eojWhTs 74 | 75 | ```cpp 76 | [[nodiscard]] constexpr auto concat(auto... args) { 77 | return (args() + ... + ""s); 78 | } 79 | ``` 80 | 81 | > https://godbolt.org/z/TeEcEWcra 82 | -------------------------------------------------------------------------------- /tips/290.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know that lambda expression is guaranteed to have a unique type?** 4 | 5 | * https://eel.is/c++draft/expr.prim.lambda 6 | 7 |

Example

8 | 9 | ```cpp 10 | static_assert(typeid([]{}) != typeid([]{})); 11 | 12 | template 13 | struct foo{}; 14 | 15 | foo f1, f2; 16 | static_assert(typeid(f1) != typeid(f2)); 17 | ``` 18 | 19 | > https://godbolt.org/z/Wrhfqba69 20 | 21 |

Puzzle

22 | 23 | * **Can you implement `create` which creates given type as a unique type or same type?** 24 | 25 | * Note: For type uniqueness inheritance might be used 26 | 27 | ```cpp 28 | class same; 29 | class unique; 30 | 31 | template 32 | [[nodiscard]] constexpr auto create(); // TODO 33 | 34 | struct foo {}; 35 | static_assert(typeid(create()) == typeid(create())); 36 | static_assert(typeid(create()) != typeid(create())); 37 | ``` 38 | 39 | > https://godbolt.org/z/YKKej87dE 40 | 41 |

Solutions

42 | 43 | ```cpp 44 | template 45 | struct wrap_unique: T {}; 46 | 47 | template 48 | [[nodiscard]] constexpr std::convertible_to auto create() 49 | { 50 | if constexpr (std::same_as) { 51 | return wrap_unique{}; 52 | } else { 53 | return T{}; 54 | } 55 | } 56 | ``` 57 | 58 | > https://godbolt.org/z/j5xvPzd47 59 | 60 | ```cpp 61 | template 62 | struct uniquify : T {}; 63 | 64 | template > 65 | [[nodiscard]] constexpr auto create() 66 | -> std::conditional_t, U, T>; 67 | ``` 68 | 69 | > https://godbolt.org/z/s6o7E4Kj6 70 | 71 | ```cpp 72 | template 73 | struct uniquify : T {}; 74 | 75 | template 76 | [[nodiscard]] constexpr auto create() { 77 | if constexpr (std::same_as) { 78 | return uniquify{}; 79 | } else { 80 | return T{}; 81 | } 82 | } 83 | ``` 84 | 85 | > https://godbolt.org/z/K8q3KdGbx 86 | 87 | ```cpp 88 | template 89 | [[nodiscard]] constexpr auto create() { 90 | if constexpr (std::is_same_v) { 91 | return UniqueLambda; 92 | } 93 | } 94 | ``` 95 | 96 | > https://godbolt.org/z/nTb5h5x4o 97 | 98 | ```cpp 99 | template 100 | [[nodiscard]] constexpr auto create() { 101 | if constexpr (std::is_same_v) { 102 | return first{}; 103 | } else { 104 | return [] {}; 105 | } 106 | } 107 | ``` 108 | 109 | > https://godbolt.org/z/Msodn95Tf 110 | 111 | ```cpp 112 | template 113 | class Holder : Tp {}; 114 | 115 | template > 116 | [[nodiscard]] constexpr auto create() { 117 | if constexpr (std::is_same_v) 118 | return Tp{}; 119 | else 120 | return Held{}; 121 | } 122 | ``` 123 | 124 | > https://godbolt.org/z/r8jcWz5ee 125 | 126 | ```cpp 127 | template 128 | class diff : T{}; 129 | 130 | template> 131 | [[nodiscard]] constexpr auto create(){ 132 | if constexpr(std::is_same_v){ 133 | return V{}; 134 | } 135 | } 136 | ``` 137 | 138 | > https://godbolt.org/z/47Wc8r5js 139 | -------------------------------------------------------------------------------- /tips/293.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know that C++17 [[nodiscard]] attribute can be applied not only to function?** 4 | 5 | * http://eel.is/c++draft/dcl.attr.nodiscard#:attribute,nodiscard 6 | 7 |

Example

8 | 9 | ```cpp 10 | struct foo { 11 | [[nodiscard]] foo(auto& resource) {} 12 | }; 13 | 14 | struct [[nodiscard]] bar {}; 15 | 16 | auto fn() -> bar; 17 | 18 | [[nodiscard]] auto fn2() -> bool; 19 | 20 | int main(int, char** argv){ 21 | foo{argv}; // ignoring temp created by [[nodiscard]] 22 | fn(); // ignoring return value with [[nodiscard]] 23 | fn2(); // ignoring return value with [[nodiscard]] 24 | } 25 | ``` 26 | 27 | > https://godbolt.org/z/Mch6cGM1h 28 | 29 |

Puzzle

30 | 31 | * **Can you mark all provided types/functions as [[nodiscard]] and handle the consequenes?** 32 | 33 | * Do you know any production use cases when applying nodiscard for non-functions improves the code? 34 | 35 | ```cpp 36 | // TODO: mark nodiscard 37 | struct foo { }; 38 | auto fn1() -> foo; 39 | auto fn2() -> bool; 40 | 41 | int main() { 42 | // TODO: handle nodiscard 43 | foo(); 44 | fn1(); 45 | fn2(); 46 | } 47 | ``` 48 | 49 | > https://godbolt.org/z/coPPe4KTP 50 | 51 |

Solutions

52 | 53 | ```cpp 54 | struct [[nodiscard]] foo { }; 55 | [[nodiscard]] auto fn1() -> foo; 56 | [[nodiscard]] auto fn2() -> bool; 57 | 58 | int main() { 59 | (void)foo(); 60 | (void)fn1(); 61 | (void)fn2(); 62 | } 63 | ``` 64 | 65 | > https://godbolt.org/z/h997hnKM4 66 | 67 | ```cpp 68 | struct [[nodiscard]] foo { }; 69 | [[nodiscard]] auto fn1() -> foo; 70 | [[nodiscard]] auto fn2() -> bool; 71 | 72 | int main() { 73 | std::ignore = foo(); 74 | std::ignore = fn1(); 75 | std::ignore = fn2(); 76 | } 77 | ``` 78 | 79 | > https://godbolt.org/z/MjjM8fdrn 80 | 81 | ```cpp 82 | struct [[nodiscard]] foo { }; 83 | [[nodiscard]] auto fn1() -> foo; 84 | [[nodiscard]] auto fn2() -> bool; 85 | 86 | int main() { 87 | auto not_ignored = foo(); 88 | auto not_ignored1 = fn1(); 89 | auto not_ignored2 = fn2(); 90 | } 91 | ``` 92 | 93 | > https://godbolt.org/z/sf9qPfj48 94 | 95 | ```cpp 96 | struct [[nodiscard]] foo { }; 97 | [[nodiscard]] auto fn1() -> foo; 98 | [[nodiscard]] auto fn2() -> bool; 99 | 100 | int main() { 101 | [[maybe_unused]] auto f = foo{}; 102 | (void) fn1(); 103 | ignore = fn2(); 104 | } 105 | ``` 106 | 107 | > https://godbolt.org/z/eja8rc4sf 108 | 109 | ```cpp 110 | struct [[nodiscard]] foo {}; 111 | auto fn1() -> foo; 112 | [[nodiscard]] auto fn2() -> bool; 113 | 114 | int main() { 115 | std::ignore = foo(); 116 | std::ignore = fn1(); 117 | std::ignore = fn2(); 118 | } 119 | ``` 120 | 121 | > https://godbolt.org/z/a73973Ge3 122 | 123 | ```cpp 124 | struct [[nodiscard]] foo { }; 125 | [[nodiscard]] auto fn1() -> foo; 126 | [[nodiscard]] auto fn2() -> bool; 127 | 128 | int main() { 129 | static_cast(foo()); 130 | [[maybe_unused]] foo Ω = fn1(); 131 | std::ignore = fn2(); 132 | } 133 | ``` 134 | 135 | > https://godbolt.org/z/1WYx6WesG 136 | 137 | ```cpp 138 | struct [[nodiscard("Attribute 1")]] foo { }; 139 | [[nodiscard("Attribute 2")]] auto fn1() -> foo; 140 | [[nodiscard("Attribute 3")]] auto fn2() -> bool; 141 | 142 | int main() { 143 | // TODO: handle nodiscard 144 | static_cast(foo()); 145 | static_cast(fn1()); 146 | auto or_this = fn2(); 147 | } 148 | ``` 149 | 150 | > https://godbolt.org/z/z5KTYG6s4 151 | 152 | ```cpp 153 | struct [[nodiscard]] foo {}; 154 | auto fn1() -> foo; 155 | [[nodiscard]] auto fn2() -> bool; 156 | 157 | int main() { 158 | // TODO: handle nodiscard 159 | static_cast(foo()); 160 | static_cast(fn1()); 161 | static_cast(fn2()); 162 | } 163 | ``` 164 | 165 | > https://godbolt.org/z/4c8rhq3W7 166 | -------------------------------------------------------------------------------- /tips/298.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know that C++23 added static operator()?** 4 | 5 | * https://wg21.link/P1169 6 | 7 |

Example

8 | 9 | ```cpp 10 | struct s { 11 | static constexpr auto operator()() { return 1; } 12 | }; 13 | auto l = [] static { return 2; }; 14 | 15 | static_assert(3 == s{}() + l()); 16 | 17 | static_assert(3 == s::operator()() + 18 | decltype(l)::operator()()); 19 | ``` 20 | 21 | > https://godbolt.org/z/43hhKcr59 22 | 23 |

Puzzle

24 | 25 | * **Can you implement count which returns number of types which can be called with static operator()?** 26 | 27 | ```cpp 28 | template 29 | constexpr auto count = 0; // TODO 30 | 31 | struct e {}; 32 | struct s { 33 | static constexpr auto operator()() -> void; 34 | }; 35 | struct ns { 36 | constexpr auto operator()() -> void; 37 | }; 38 | 39 | auto l = [] { }; 40 | auto sl = [] static {}; 41 | 42 | static_assert(0 == count); 43 | static_assert(1 == count); 44 | static_assert(0 == count); 45 | static_assert(0 == count); 46 | static_assert(1 == count); 47 | static_assert(2 == count); 48 | ``` 49 | 50 | > https://godbolt.org/z/McrMfasjq 51 | 52 |

Solutions

53 | 54 | ```c++ 55 | template 56 | constexpr auto count = (requires { (void*)&Ts::operator(); } + ... + 0); 57 | ``` 58 | 59 | > https://godbolt.org/z/Td6zxxaoM 60 | 61 | ```c++ 62 | template 63 | constexpr auto count = (0 + ... + requires{Ts::operator();}); 64 | ``` 65 | 66 | > https://godbolt.org/z/P74br35qh 67 | 68 | ```cpp 69 | template 70 | constexpr auto count = (... + requires { Ts::operator()(); }); 71 | ``` 72 | 73 | > https://godbolt.org/z/KoP4sxrxr 74 | 75 | ```cpp 76 | template 77 | concept is_static_call_operator = requires{T::operator()();}; 78 | 79 | template 80 | constexpr auto count = (0 + ... + is_static_call_operator) 81 | ``` 82 | 83 | > https://godbolt.org/z/TEPKG3xa8 84 | 85 | ```cpp 86 | template 87 | constexpr auto count = 88 | (0 + ... + requires{Ts::operator()();}) 89 | ``` 90 | 91 | > https://godbolt.org/z/f1xKoEvKz 92 | 93 | ```cpp 94 | template 95 | constexpr auto has_static_operator = [](){ 96 | if constexpr (requires { T::operator();}){ 97 | return 1; 98 | } 99 | else{ 100 | return 0; 101 | } 102 | }(); 103 | 104 | template 105 | constexpr auto count = [](){ 106 | return 0 + (... + has_static_operator); 107 | }(); 108 | ``` 109 | 110 | > https://godbolt.org/z/j3a6o78Wf 111 | 112 | ```cpp 113 | template 114 | concept HasStaticOp = requires(T) { T::operator(); }; 115 | 116 | template 117 | constexpr auto count = (HasStaticOp + ...); 118 | ``` 119 | 120 | > https://godbolt.org/z/Yz53ebYn9 121 | 122 | ```cpp 123 | template 124 | constexpr auto count = [] -> int { 125 | return (requires {Ts::operator()();}+...); 126 | }(); 127 | ``` 128 | 129 | > https://godbolt.org/z/ne73vMMPW 130 | -------------------------------------------------------------------------------- /tips/299.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know that C++20 concepts can be used to avoid implicit conversions?** 4 | 5 | * https://eel.is/c++draft/concept.same 6 | 7 |

Example

8 | 9 | ```cpp 10 | #include 11 | 12 | void foo(int i); 13 | void bar(std::same_as auto i); 14 | 15 | 16 | int main() { 17 | foo(42.0); // implicit conversion 18 | bar(42.0); // error: contraint not satisifed 19 | } 20 | ``` 21 | 22 | > https://godbolt.org/z/v45nYdaT7 23 | 24 |

Puzzle

25 | 26 | * **Can you implement conversion safe `invoke[args...]`?** 27 | 28 | * Note: Doesn't allow implicit conversions neither for parameters nor for the return type 29 | 30 | ```cpp 31 | struct { 32 | template 33 | [[nodiscard]] constexpr auto operator[](...); // TODO 34 | } invoke; 35 | 36 | auto foo(int, double, float) -> void; 37 | 38 | constexpr auto can_invoke_with = [](R fn(Ts...), auto&&... args) { 39 | return requires { { invoke.template operator[](fn, std::forward(args)...) } -> std::same_as; }; 40 | }; 41 | 42 | static_assert( can_invoke_with(foo, int{}, double{}, float{})); 43 | static_assert(not can_invoke_with(foo, int{}, float{}, float{})); 44 | static_assert(not can_invoke_with(foo, float{}, float{}, float{})); 45 | static_assert(not can_invoke_with(foo, short{}, int{}, double{}, float{})); 46 | static_assert(not can_invoke_with(foo, int{}, double{}, float{}, float{})); 47 | ``` 48 | 49 | > https://godbolt.org/z/vz9sxssE4 50 | 51 |

Solutions

52 | 53 | ```cpp 54 | template 55 | [[nodiscard]] constexpr auto operator[](R fn(Ts...), std::same_as auto... ts ) -> R; 56 | ``` 57 | 58 | > https://godbolt.org/z/T4rvx87Wo 59 | 60 | ```cpp 61 | template && ...), int> = 0> 64 | [[nodiscard]] constexpr auto operator[](R fn(Ts...), Ts2... ts ) -> R; 65 | ``` 66 | > https://godbolt.org/z/7fecsaErf 67 | 68 | ```cpp 69 | struct { 70 | template 71 | [[nodiscard]] constexpr auto operator[](R fn(Ts...), 72 | std::same_as auto&&... args) 73 | -> std::same_as decltype(auto) { 74 | return fn(std::forward(args)...); 75 | } 76 | } invoke; 77 | ``` 78 | 79 | > https://godbolt.org/z/cbGrTW74q 80 | 81 | ```cpp 82 | struct { 83 | template 84 | [[nodiscard]] constexpr auto operator[](R fn(Ts...), std::same_as auto&&... args){ 85 | return fn(std::forward(args)...); 86 | } 87 | } invoke; 88 | ``` 89 | 90 | > https://godbolt.org/z/53jaMTK6z 91 | 92 | ```cpp 93 | struct { 94 | template 95 | [[nodiscard]] constexpr auto operator[](auto&& fn, std::same_as auto&&... args) -> std::same_as auto { 96 | return fn(std::forward(args)...); 97 | } 98 | } invoke; 99 | ``` 100 | 101 | > https://godbolt.org/z/Gjv51ev13 102 | 103 | ```cpp 104 | struct { 105 | template 106 | [[nodiscard]] constexpr auto operator[](auto fn, std::same_asauto ...params) -> std::same_as auto { 107 | return fn(params...); 108 | }; 109 | } invoke; 110 | ``` 111 | 112 | > https://godbolt.org/z/9vMh8sje6 113 | 114 | ```cpp 115 | struct { 116 | template 117 | [[nodiscard]] constexpr auto operator[](auto F, auto... args) -> std::same_as auto requires (std::same_as && ...) { 118 | return F(args...); 119 | }; 120 | } invoke; 121 | ``` 122 | 123 | > https://godbolt.org/z/TjbzWd7dT 124 | 125 | ```cpp 126 | 127 | struct { 128 | template 129 | [[nodiscard]] constexpr auto operator[](R fn(Ts...), std::same_as auto&& ... args)->R; 130 | } invoke; 131 | ``` 132 | 133 | > https://godbolt.org/z/bz38Yb3c6 134 | 135 | ```cpp 136 | struct { 137 | template 138 | [[nodiscard]] constexpr auto operator[](auto f, std::same_as auto &&...args){ 139 | f(args...); 140 | } 141 | } invoke; 142 | ``` 143 | 144 | > https://godbolt.org/z/MP9nK16xr 145 | -------------------------------------------------------------------------------- /tips/302.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you now that with concepts you can override a type?** 4 | 5 | * https://eel.is/c++draft/temp.concept#def:concept 6 | 7 |

Example

8 | 9 | ```cpp 10 | template struct foo { 11 | static constexpr auto value = N; 12 | }; 13 | static_assert(42 == foo<42>::value); 14 | 15 | template 16 | requires true // override -> more specialized 17 | struct foo { 18 | static constexpr auto value = 0; 19 | }; 20 | static_assert(42 == foo<42>::value); // memoized 21 | static_assert(0 == foo<43>::value); 22 | ``` 23 | 24 | > https://godbolt.org/z/hPcsKG4an 25 | 26 |

Puzzle

27 | 28 | * **Can you override std::shared_ptr to avoid thred-safe guards?** 29 | 30 | ```cpp 31 | #include 32 | 33 | // TODO override shared_ptr with std::__shared_ptr which is is not thread-safe 34 | // NOTE overriding STL is UB 35 | // Alternative - boost::local_shared_ptr 36 | 37 | #include 38 | static_assert(std::is_base_of_v, std::shared_ptr>); 39 | ``` 40 | 41 | > https://godbolt.org/z/7axP5or3q 42 | 43 |

Solutions

44 | 45 | ```cpp 46 | namespace std { 47 | template requires ::std::is_same_v 48 | class shared_ptr : public __shared_ptr {}; 49 | } 50 | ``` 51 | 52 | > https://godbolt.org/z/Mhj1M7Yec 53 | 54 | ```cpp 55 | namespace std { 56 | template 57 | requires std::is_integral_v 58 | class shared_ptr : public __shared_ptr {}; 59 | } // namespace std 60 | ``` 61 | 62 | > https://godbolt.org/z/sa9a937of 63 | 64 | ```cpp 65 | namespace boost{ 66 | template requires std::is_same_v 67 | struct shared_ptr : std::__shared_ptr { 68 | }; 69 | } 70 | 71 | static_assert(std::is_base_of_v, boost::shared_ptr>); 72 | ``` 73 | 74 | > https://godbolt.org/z/acWKbG4fG 75 | 76 | ```cpp 77 | namespace std{ 78 | template requires is_same_v 79 | struct shared_ptr : std::__shared_ptr { 80 | }; 81 | } 82 | 83 | static_assert(std::is_base_of_v, std::shared_ptr>); 84 | ``` 85 | 86 | > https://godbolt.org/z/z7YznT45n 87 | 88 | 89 | ```cpp 90 | template 91 | concept integral = std::is_integral_v; 92 | 93 | namespace std{ 94 | template 95 | class shared_ptr : public std::__shared_ptr{ 96 | }; 97 | } 98 | 99 | #include 100 | static_assert(std::is_base_of_v, std::shared_ptr>); 101 | ``` 102 | 103 | > https://godbolt.org/z/PP6KEczMb 104 | 105 | ```cpp 106 | template 107 | requires true 108 | class std::shared_ptr : std::__shared_ptr { 109 | }; 110 | 111 | #include 112 | static_assert(std::is_base_of_v, std::shared_ptr>); 113 | ``` 114 | 115 | > https://godbolt.org/z/rrr8GP3qe 116 | -------------------------------------------------------------------------------- /tips/303.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you about typename erasure technique to reduce compilation times with templates?** 4 | 5 | * https://eel.is/c++draft/#temp 6 | 7 |

Example

8 | 9 | ```cpp 10 | template struct long_type {}; 11 | 12 | using long_type_t = decltype( 13 | [](std::index_sequence) { 14 | return long_type{}; 15 | } 16 | (std::make_index_sequence<42>{}) 17 | ); 18 | 19 | // typename erasure 20 | struct short_type_t : long_type_t{}; 21 | 22 | // generic typename erasure 23 | template using typename_erasure = decltype([] { 24 | struct s : T { using T::T; }; // generated in the compiler as `$0::s` or similar 25 | return s{}; 26 | }()); 27 | 28 | int main() { 29 | assert(std::string_view{typeid(long_type_t).name()}.size() > 100); // long... 30 | assert(std::string_view{typeid(short_type_t).name()}.size() < 15); // short 31 | assert(std::string_view{typeid(typename_erasure).name()}.size() < 25); // short 32 | } 33 | ``` 34 | 35 | > https://godbolt.org/z/heaKzY7To 36 | 37 |

Puzzle

38 | 39 | * **Can you implement typename_erasure function which will erase all given types?** 40 | 41 | ```cpp 42 | template 43 | [[nodiscard]] constexpr auto typename_erasure; // TODO 44 | 45 | 46 | template struct long_type{ }; 47 | template using long_type_t = decltype( 48 | [](std::index_sequence) { 49 | return long_type{}; 50 | } 51 | (std::make_index_sequence{}) 52 | ); 53 | 54 | #include 55 | #include 56 | [[nodiscard]] consteval auto type_name(auto) { return std::string_view{__PRETTY_FUNCTION__}; } 57 | 58 | static_assert(std::size(type_name(typename_erasure, long_type_t<200>>())) < 42); 59 | static_assert(not std::is_base_of_v, decltype(typename_erasure, long_type_t<200>>())>); 60 | static_assert(std::is_base_of_v, decltype(typename_erasure, long_type_t<200>>())>); 61 | static_assert(std::is_base_of_v, decltype(typename_erasure, long_type_t<200>>())>); 62 | ``` 63 | 64 | > https://godbolt.org/z/zhd716b3T 65 | 66 |

Solutions

67 | 68 | ```cpp 69 | template 70 | [[nodiscard]] constexpr auto typename_erasure() 71 | { 72 | struct s : Ts... { 73 | s() = default; 74 | s(Ts &&...arg) : Ts(::std::forward(arg)) ... {} 75 | }; 76 | return s{}; 77 | } 78 | ``` 79 | 80 | > https://godbolt.org/z/xjoe54Mxh 81 | 82 | ```cpp 83 | template 84 | [[nodiscard]] constexpr auto typename_erasure(){ 85 | struct s : public Ts...{}; 86 | return s{}; 87 | }; 88 | ``` 89 | 90 | > https://godbolt.org/z/e1zPh5GGs 91 | 92 | ```cpp 93 | template using typename_erasure_one = decltype([] { 94 | struct s : T { using T::T; }; 95 | return s{}; 96 | }()); 97 | 98 | template 99 | [[nodiscard]] constexpr auto typename_erasure = [] { 100 | struct a : typename_erasure_one...{}; 101 | return a{}; 102 | }; 103 | ``` 104 | 105 | > https://godbolt.org/z/8hxM5h7a5 106 | -------------------------------------------------------------------------------- /tips/307.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know that C++23 added static operator[]?** 4 | 5 | * https://wg21.link/P2589 6 | 7 |

Example

8 | 9 | ```cpp 10 | struct s { 11 | static constexpr auto operator[]() { return 42; } 12 | }; 13 | 14 | static_assert(42 == s{}[]); 15 | static_assert(42 == s::operator[]()); 16 | ``` 17 | 18 | > https://godbolt.org/z/GzTc3MxPE 19 | 20 |

Puzzle

21 | 22 | * Can you provide an example which shows that `static operator[]` requies less assembly instructions than `non-static` version? 23 | 24 |

Solutions

25 | 26 | ```cpp 27 | struct foo { 28 | static auto operator[]() -> int; 29 | }; 30 | 31 | struct bar { 32 | auto operator[]() const -> int; 33 | }; 34 | 35 | auto get(const int, const foo &o) { return o[]; } 36 | auto get(const int, const bar &o) { return o[]; } 37 | ``` 38 | 39 | > https://godbolt.org/z/ThP4jnoa7 40 | 41 | ```cpp 42 | struct X { static void operator[](); }; 43 | struct Y { void operator[]() const; }; 44 | 45 | auto fn1() { X{}[]; } 46 | auto fn2() { Y{}[]; } 47 | ``` 48 | 49 | > https://godbolt.org/z/e4xcG9aKv 50 | 51 | ```cpp 52 | struct s { 53 | static constexpr auto operator[]() { return 42; } 54 | }; 55 | 56 | static_assert(42 == s{}[]); 57 | static_assert(42 == s::operator[]()); 58 | 59 | struct ns { 60 | constexpr auto operator[](){ return 42; } 61 | }; 62 | 63 | static_assert(42 == ns{}[]); 64 | 65 | int main() { 66 | s{}[]; 67 | ns{}[]; 68 | 69 | }; 70 | ``` 71 | 72 | > https://godbolt.org/z/cEq6ET3W5 73 | 74 | ```cpp 75 | struct foo { 76 | static auto operator[]() -> int; 77 | }; 78 | 79 | struct bar { 80 | auto operator[]() const -> int; 81 | }; 82 | 83 | template 84 | struct getter 85 | { 86 | int operator()() const 87 | { 88 | return std::apply([](Ts const &... args ){ return (args[] +...);},t); 89 | } 90 | std::tuple t; 91 | }; 92 | 93 | int get(getter const & g){ return g();} 94 | int get(getter const & g){ return g();} 95 | ``` 96 | 97 | > https://godbolt.org/z/obxWar47n 98 | 99 | ```cpp 100 | // static version 101 | struct s { 102 | static constexpr auto operator[](int i) { return 42; } 103 | }; 104 | int main(){ 105 | return s::operator[](0); 106 | } 107 | ``` 108 | 109 | > https://godbolt.org/z/bjKccWG7M 110 | 111 | ```cpp 112 | // static version 113 | struct s { 114 | static void operator[](); 115 | }; 116 | auto fn(){ 117 | s::operator[](); 118 | } 119 | ``` 120 | 121 | > https://godbolt.org/z/4zxYP5WaG 122 | -------------------------------------------------------------------------------- /tips/309.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know that C++20 added support for constexpr std::vector?** 4 | 5 | * https://wg21.link/P1004 6 | 7 |

Example

8 | 9 | ```cpp 10 | template consteval auto fn() { 11 | std::vector v{Ns...}; 12 | return std::size(v); 13 | } 14 | 15 | static_assert(3uz == fn<1, 2, 3>()); 16 | ``` 17 | 18 | > https://godbolt.org/z/vx6cPaY8s 19 | 20 |

Puzzle

21 | 22 | > **Can you simplify `filter` example by applying best C++ practicies?** 23 | 24 | ```cpp 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | constexpr auto filter(auto t, auto fn) { 31 | // get the size of filtered elements 32 | constexpr auto size = [=] { 33 | std::vector v{}; 34 | std::apply([&, i = 0](auto... ts) mutable { 35 | ([&] { 36 | if (fn(ts)) { 37 | v.push_back(i); 38 | } 39 | i++; 40 | }(), ...); 41 | }, t()); 42 | return v.size(); 43 | }(); 44 | 45 | // get the array of indecies of filtered elements 46 | constexpr auto values = [=] { 47 | std::array values{}; 48 | std::apply([&, x = 0, i = 0](auto... ts) mutable { 49 | ([&] { 50 | if (fn(ts)) { 51 | values[x++] = i; 52 | } 53 | i++; 54 | }(), ...); 55 | }, t()); 56 | return values; 57 | }(); 58 | 59 | // convert array indicies to tuple elements 60 | return [=](std::index_sequence) { 61 | return std::tuple{std::get(t())...}; 62 | }(std::make_index_sequence{}); 63 | }; 64 | 65 | static_assert(std::tuple{1, 2, 3} == filter([] { return std::tuple{1, 2, 3}; }, [](auto) { return true; })); 66 | static_assert(std::tuple{} == filter([] { return std::tuple{1, 2, 3}; }, [](auto) { return false; })); 67 | static_assert(std::tuple{1, 3} == filter([] { return std::tuple{1, 2, 3}; }, [](auto v) { return v != 2; })); 68 | static_assert(std::tuple{2} == filter([] { return std::tuple{1, 2, 3}; }, [](auto v) { return v == 2; })); 69 | ``` 70 | 71 | > https://godbolt.org/z/E7rTGdn7b 72 | 73 |

Solutions

74 | 75 | ```cpp 76 | constexpr auto filter(auto t, auto fn) { 77 | // get the vector of filtered elements 78 | constexpr auto r = [=] { 79 | return std::apply( 80 | [=](auto... ts) { 81 | return [=] { 82 | auto v = std::vector{ts...}; 83 | std::erase_if(v, std::not_fn(fn)); 84 | return v; 85 | }; 86 | }, 87 | t()); 88 | }(); 89 | 90 | // convert the vector to a tuple 91 | return [=](std::index_sequence) { 92 | const auto v = r(); 93 | return std::tuple{v[Ns]...}; 94 | }(std::make_index_sequence{}); 95 | } 96 | ``` 97 | 98 | > https://godbolt.org/z/WTMEPs93o 99 | 100 | ```cpp 101 | constexpr auto filter(auto t, auto fn) { 102 | constexpr auto v = std::apply( 103 | [=](auto... ts) { 104 | auto const ints = {ts...}; 105 | return [=] () { return ints | std::views::filter(fn) | std::ranges::to(); }; 106 | }, t()); 107 | 108 | return [=](std::index_sequence) { 109 | return std::tuple{v()[Ns]...}; 110 | }(std::make_index_sequence{}); 111 | } 112 | ``` 113 | 114 | > https://godbolt.org/z/3z3s4s6h6 115 | -------------------------------------------------------------------------------- /tips/310.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know that C+23 permitts static constexpr variables in constexpr functions?** 4 | 5 | * https://wg21.link/P2647 6 | 7 |

Example

8 | 9 | ```cpp 10 | constexpr auto foo() { 11 | static constexpr auto value = 42; // error in C++20, okay in C++23 12 | return value; 13 | } 14 | ``` 15 | 16 | > https://godbolt.org/z/6zK6vj91z 17 | 18 |

Puzzle

19 | 20 | > **Can you implement `to_string_view` which converts in-place given characters to string_view?** 21 | 22 | ```cpp 23 | template 24 | [[nodiscard]] constexpr auto to_string_view(); // TODO 25 | 26 | static_assert(std::string_view{""} == to_string_view<>()); 27 | static_assert(std::string_view{"42"} == to_string_view<'4', '2'>()); 28 | static_assert(std::string_view{"foo"} == to_string_view<'f', 'o', 'o'>()); 29 | static_assert(std::string_view{"bar"} == to_string_view<'b', 'a', 'r'>()); 30 | ``` 31 | 32 | > https://godbolt.org/z/4fbYGGxco 33 | 34 |

Solutions

35 | 36 | ```cpp 37 | template 38 | [[nodiscard]] constexpr auto to_string_view(){ 39 | static constexpr char p[sizeof...(Cs)] = {Cs...}; 40 | return std::string_view(p, sizeof...(Cs)); 41 | } 42 | ``` 43 | 44 | > https://godbolt.org/z/r31M7hhno 45 | 46 | ```cpp 47 | template 48 | 49 | [[nodiscard]] constexpr auto to_string_view(){ 50 | static constexpr std::array chars{Cs...}; 51 | return std::string_view{chars}; 52 | } 53 | 54 | template<> 55 | [[nodiscard]] constexpr auto to_string_view<>(){ 56 | return std::string_view{}; 57 | } 58 | ``` 59 | 60 | > https://godbolt.org/z/eTWasjTTr 61 | 62 | ```cpp 63 | [[nodiscard]] constexpr auto to_string_view() 64 | { 65 | constexpr static std::size_t N = sizeof...(Cs); 66 | if constexpr (N >0) 67 | { 68 | constexpr static std::array chars{Cs...}; 69 | return std::string_view(chars.data(),N); 70 | } else 71 | return std::string_view(""); 72 | } 73 | ``` 74 | 75 | > https://godbolt.org/z/9MMKv98dE 76 | 77 | ```cpp 78 | template 79 | [[nodiscard]] constexpr auto to_string_view() { 80 | static constexpr char cs[]{Cs..., 0}; 81 | return std::string_view{cs}; 82 | } 83 | ``` 84 | 85 | > https://godbolt.org/z/PPdxETv76 86 | -------------------------------------------------------------------------------- /tips/312.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * Did you know that C++20 added support for Unevaluated asm-declaration in constexpr functions? 4 | 5 | * https://wg21.link/P1668 6 | 7 |

Example

8 | 9 | ```cpp 10 | constexpr auto get = [](auto value) { 11 | if consteval { 12 | return value; 13 | } else { 14 | auto result = 0; 15 | asm("movl $42, %%eax\n" : "=r" (result) ); 16 | return result; 17 | } 18 | }; 19 | 20 | static_assert(0 == get(0)); 21 | static_assert(4 == get(4)); 22 | static_assert(2 == get(2)); 23 | 24 | consteval auto fn() { 25 | return get(0); 26 | } 27 | 28 | int main(int argc, char**) { 29 | assert(42 == get(0)); 30 | assert(42 == get(argc)); 31 | return fn(); 32 | } 33 | ``` 34 | 35 | > https://godbolt.org/z/z5jYdsa9P 36 | 37 |

Puzzle

38 | 39 | > Can you implement `const_sub_or_asm_add` which subtract given numbers using C++ operators if values can be constant evaluated and adds them using inline assembly otherwise? 40 | 41 | ```cpp 42 | constexpr auto const_sub_or_asm_add = [](auto lhs, auto rhs) { 43 | return 0; /*TODO*/ 44 | }; 45 | 46 | static_assert(1 == const_sub_or_asm_add(2, 1)); 47 | static_assert(-1 == const_sub_or_asm_add(3, 4)); 48 | 49 | int main(int argc, char**) { 50 | { 51 | constexpr auto c = const_sub_or_asm_add(1, 2); 52 | assert(-1 == c); 53 | } 54 | 55 | { 56 | auto c = const_sub_or_asm_add(1, 2); 57 | assert(3 == c); 58 | } 59 | 60 | { 61 | assert(3 == const_sub_or_asm_add(1, 2)); 62 | assert(1 == const_sub_or_asm_add(argc, 0)); 63 | assert(2 == const_sub_or_asm_add(argc, argc)); 64 | } 65 | } 66 | ``` 67 | 68 | > https://godbolt.org/z/897a3fn8M 69 | 70 | 71 |

Solutions

72 | 73 | ```cpp 74 | constexpr auto const_sub_or_asm_add = [](auto lhs, auto rhs) { 75 | if consteval { 76 | return lhs - rhs; 77 | } else { 78 | auto result = 0; 79 | asm ("addl %%ebx,%%eax" 80 | :"=a"(result) 81 | :"a"(lhs), "b"(rhs) 82 | ); 83 | return result; 84 | } 85 | }; 86 | ``` 87 | 88 | > https://godbolt.org/z/TdjMzKdaT 89 | 90 | ```cpp 91 | constexpr auto const_sub_or_asm_add = [](auto lhs, auto rhs) { 92 | if consteval { 93 | return lhs - rhs; 94 | } else { 95 | decltype(lhs + rhs) result; 96 | asm("leal (%1, %2), %0\n" : "=r"(result) : "r"(lhs), "r"(rhs)); 97 | return result; 98 | } 99 | }; 100 | ``` 101 | 102 | > https://godbolt.org/z/vrf35veev 103 | 104 | ```cpp 105 | constexpr auto const_sub_or_asm_add = [](auto lhs, auto rhs) { 106 | if consteval { 107 | return lhs - rhs; 108 | } else { 109 | auto result = 0; 110 | asm("add %%edx, %%eax\n" : "=r" (result) : "edx" (lhs), "eax" (rhs) ); 111 | return result; 112 | } 113 | }; 114 | ``` 115 | 116 | > https://godbolt.org/z/f34bsrj4o 117 | 118 | ```cpp 119 | constexpr auto const_sub_or_asm_add = [](auto lhs, auto rhs) { 120 | if consteval { 121 | return lhs - rhs; 122 | } else { 123 | auto result = 0; 124 | asm("movl %2, %0;" 125 | "addl %1, %0;" 126 | : "=&r"(result) 127 | : "r"(lhs), "r"(rhs)); 128 | return result; 129 | } 130 | }; 131 | ``` 132 | 133 | > https://godbolt.org/z/bz4z5Exdd 134 | 135 | ```cpp 136 | constexpr auto const_sub_or_asm_add = [](auto lhs, auto rhs) { 137 | if (std::is_constant_evaluated()) 138 | return lhs - rhs; 139 | asm("addl %%ebx, %%eax;" : "=a" (lhs) : "a" (lhs) , "b" (rhs) ); 140 | return lhs; 141 | }; 142 | ``` 143 | 144 | > https://godbolt.org/z/af3vonrnW 145 | 146 | ```cpp 147 | constexpr auto const_sub_or_asm_add = [](auto lhs, auto rhs) { 148 | if consteval { 149 | return lhs - rhs; 150 | } else { 151 | auto add{0}; 152 | asm("addl %%ebx, %%eax\n" 153 | : "=a" (add) 154 | : "a" (lhs), "b" (rhs) ); 155 | return lhs + rhs; 156 | } 157 | return 0; /*TODO*/ 158 | }; 159 | ``` 160 | 161 | > https://godbolt.org/z/7zadcr6qx 162 | -------------------------------------------------------------------------------- /tips/313.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * Did you know that C++26 added #embed? 4 | 5 | * https://wg21.link/P1967 6 | 7 |

Example

8 | 9 | ```cpp 10 | #include 11 | 12 | static constexpr char self[] = { 13 | #embed __FILE__ 14 | }; 15 | 16 | static_assert(std::string_view{std::cbegin(self), std::cend(self)}.front() == '#'); 17 | static_assert(std::string_view{std::cbegin(self), std::cend(self)}.back() == ';'); 18 | ``` 19 | 20 | > https://godbolt.org/z/czTaz578j 21 | 22 |

Puzzle

23 | 24 | > Can you implement `meta_contains` which returns true if given string is contined in the current file and false otherwise? 25 | 26 | ```cpp 27 | template 28 | constexpr auto meta_contains = false; // TODO 29 | 30 | struct foo {}; 31 | struct bar {}; 32 | 33 | auto fn() -> void; 34 | 35 | static_assert(not meta_contains<"struct x">); 36 | static_assert(not meta_contains<"STD::string_view">); 37 | static_assert(meta_contains<"std::string_view">); 38 | static_assert(meta_contains<"struct foo">); 39 | static_assert(meta_contains<"struct bar">); 40 | static_assert(meta_contains<"auto fn()">); 41 | ``` 42 | 43 | > https://godbolt.org/z/6cvn4n8j5 44 | 45 |

Solutions

46 | 47 | ```cpp 48 | #include 49 | #include 50 | template 51 | struct FixedString { 52 | char buf[N + 1]{}; 53 | constexpr FixedString(char const* s) { 54 | for (unsigned i = 0; i != N; ++i) buf[i] = s[i]; 55 | } 56 | constexpr char const* data() const { return buf; } 57 | constexpr auto size() const { return N; } 58 | }; 59 | template FixedString(char const (&)[N]) -> FixedString; 60 | 61 | using cstringtype = char const []; 62 | 63 | constexpr static cstringtype metadata { 64 | #embed __FILE__ 65 | }; 66 | 67 | std::string_view meta {metadata}; 68 | 69 | struct foo {}; 70 | struct bar {}; 71 | 72 | auto fn() -> void; 73 | 74 | template 75 | constexpr auto meta_contains = std::search( 76 | metadata, metadata + std::size(metadata), 77 | Name.data(), Name.data() + Name.size() 78 | ) <= std::find(metadata, metadata + std::size(metadata), '/'); //an ENDTAG before static asserts 79 | 80 | // 81 | ``` 82 | 83 | > https://godbolt.org/z/xfWh649E6 84 | 85 | ```cpp 86 | template 87 | constexpr auto meta_contains = [] { 88 | static constexpr char meta[] = { 89 | #embed __FILE__ 90 | }; 91 | const auto code = std::string_view(std::data(meta), std::size(meta)); 92 | const auto find = code.find(Name); 93 | return find != std::string_view::npos and code[find-1] != '\"'; 94 | }(); 95 | ``` 96 | 97 | > https://godbolt.org/z/Kq5eWfGaK 98 | 99 | ```cpp 100 | constexpr fixed_string self = { 101 | #embed __FILE__ 102 | }; 103 | 104 | template 105 | constexpr auto meta_contains = std::string_view{self}.substr(0,1600).contains(Name); 106 | ``` 107 | 108 | > https://godbolt.org/z/YjsreKvPd 109 | 110 | ```cpp 111 | static constexpr char self[] = { 112 | #embed __FILE__ 113 | }; 114 | 115 | template 116 | constexpr auto meta_contains = [](const char (&f)[N]) constexpr{ 117 | std::string_view code (f,N); 118 | auto found = code.find(Name); 119 | return found >=0 && (found ==0||code[found-1] != '"') ; 120 | }(self); 121 | ``` 122 | 123 | > https://godbolt.org/z/dq1nPWes1 124 | 125 | 126 | ```cpp 127 | constexpr auto marker = fixed_string{{'s','t','a','t','i','c','_','a','s','s','e','r','t'}}; 128 | constexpr auto marker_location = std::string_view{std::begin(self), std::end(self)}.find(std::string_view{marker.begin(), marker.end()-1}); 129 | 130 | template 131 | constexpr auto meta_contains = 132 | std::string_view{std::begin(self), std::end(self)} 133 | .find(std::string_view{Name.begin(), Name.end()-1}) < 134 | marker_location; 135 | ``` 136 | 137 | > https://godbolt.org/z/q3q5Gcxax 138 | -------------------------------------------------------------------------------- /tips/314.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * Did you know that with gnu:C++26 a more parts of static reflection can be emulated? 4 | 5 | * https://wg21.link/P1240 6 | 7 |

Example

8 | 9 | ```cpp 10 | struct foo { int a{}; int b{}; }; 11 | struct bar { int a{}; }; 12 | 13 | constexpr auto structs = meta_find<"struct">; 14 | static_assert(2uz == structs.size()); 15 | static_assert("foo" == structs[0]); 16 | static_assert("bar" == structs[1]); 17 | 18 | static_assert(reflexpr); 19 | 20 | constexpr auto f = typeof<[] { return structs[0]; }>{.a = 1, .b = 2}; 21 | static_assert(1 == f.a and 2 == f.b); 22 | 23 | constexpr auto t = to_tuple(f); 24 | static_assert("a" == std::get<0>(t).name and 1 == std::get<0>(t).value); 25 | static_assert("b" == std::get<1>(t).name and 2 == std::get<1>(t).value); 26 | 27 | int main() { 28 | reflexpr auto b = bar{}; 29 | // ... 30 | constexpr auto name = std::find(structs.cbegin(), structs.cend(), type_name()); 31 | static_assert(name != structs.cend()); 32 | static_assert(42 == typeof<[] { return *name; }>{42}.a); 33 | } 34 | ``` 35 | 36 | > https://godbolt.org/z/PPsn4KM7Y 37 | 38 |

Puzzle

39 | 40 | > **Given reflection primitives can you implemnt all_structs_field_names which returns combined list of all field names from all structs in the file?** 41 | 42 | ```cpp 43 | struct foo { 44 | int a{}; 45 | int b{}; 46 | }; 47 | static_assert(reflexpr); 48 | 49 | struct bar { 50 | int c{}; 51 | }; 52 | static_assert(reflexpr); 53 | 54 | struct baz { 55 | int d{}; 56 | int e{}; 57 | int f{}; 58 | }; 59 | static_assert(reflexpr); 60 | 61 | [[nodiscard]] constexpr auto all_structs_field_names(); // TODO 62 | 63 | constexpr auto ns = all_structs_field_names(); 64 | static_assert(6uz == ns.size()); 65 | static_assert("a" == ns[0]); 66 | static_assert("b" == ns[1]); 67 | static_assert("c" == ns[2]); 68 | static_assert("d" == ns[3]); 69 | static_assert("e" == ns[4]); 70 | static_assert("f" == ns[5]); 71 | ``` 72 | 73 | > https://godbolt.org/z/M1KdE8xET 74 | 75 |

Solutions

76 | 77 | ```cpp 78 | [[nodiscard]] constexpr auto all_structs_field_names() { 79 | return [](std::index_sequence) { 80 | return std::apply( 81 | [](auto... elements) { 82 | static_vector v{}; 83 | (..., v.push_back(elements.name)); 84 | return v; 85 | }, 86 | std::tuple_cat( 87 | to_tuple(typeof<[] { return meta_find<"struct">[Is]; }>{})...)); 88 | }(std::make_index_sequence.size()>{}); 89 | } 90 | ``` 91 | 92 | 93 | > https://godbolt.org/z/hnKTfs8bx 94 | 95 | ```cpp 96 | [[nodiscard]] constexpr auto members_to_names(auto& out, auto struct_members) { 97 | constexpr auto num_members = std::tuple_size_v; 98 | [&out, &struct_members](std::index_sequence) { 99 | ( 100 | out.push_back(std::get(struct_members).name) 101 | , ... 102 | ); 103 | }(std::make_index_sequence{}); 104 | } 105 | 106 | template 107 | 108 | [[nodiscard]] constexpr auto struct_instance() { 109 | return typeof<[] { return structs[index]; }>{}; 110 | }; 111 | 112 | template 113 | 114 | [[nodiscard]] constexpr auto all_structs_field_names() { 115 | static_vector names{}; 116 | [&names](std::index_sequence ns) { 117 | ( 118 | members_to_names(names, to_tuple(struct_instance())) 119 | , ... 120 | ); 121 | }(std::make_index_sequence{}); 122 | return names; 123 | } 124 | ``` 125 | 126 | > https://godbolt.org/z/z56h5xahv 127 | -------------------------------------------------------------------------------- /tips/315.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know about C++20 `is_layout_compatible_v` type_trait?** 4 | 5 | * https://eel.is/c++draft/meta.type.synop 6 | 7 |

Example

8 | 9 | ```cpp 10 | struct foo { 11 | int a{}; 12 | int b{}; 13 | }; 14 | 15 | struct bar { 16 | const int x{}; 17 | int y{}; 18 | }; 19 | 20 | struct baz : bar { }; 21 | 22 | struct other { 23 | int a{}; 24 | char b[4]{}; 25 | }; 26 | 27 | static_assert(not std::is_layout_compatible_v); 28 | static_assert(not std::is_layout_compatible_v); 29 | 30 | static_assert(std::is_layout_compatible_v); 31 | static_assert(std::is_layout_compatible_v); 32 | static_assert(std::is_layout_compatible_v); 33 | static_assert(std::is_layout_compatible_v); 34 | static_assert(not std::is_layout_compatible_v); 35 | static_assert(not std::is_layout_compatible_v); 36 | ``` 37 | 38 | > https://godbolt.org/z/v9KM34qMW 39 | 40 |

Puzzle

41 | 42 | > **Can you implemtn `count_compatible` which returns the number of compatible types to the given one?** 43 | 44 | ```cpp 45 | template 46 | constexpr auto count_compatible = 0; // TODO 47 | 48 | static_assert(0 == count_compatible); 49 | static_assert(0 == count_compatible); 50 | static_assert(0 == count_compatible); 51 | 52 | static_assert(1 == count_compatible); 53 | static_assert(1 == count_compatible); 54 | static_assert(2 == count_compatible); 55 | static_assert(4 == count_compatible); 56 | ``` 57 | 58 | > https://godbolt.org/z/7cjqdxGxE 59 | 60 |

Solutions

61 | 62 | ```cpp 63 | template 64 | constexpr auto count_compatible = (std::is_layout_compatible_v + ... + std::size_t{}); 65 | ``` 66 | > https://godbolt.org/z/8v741635E 67 | 68 | ```cpp 69 | template 70 | constexpr auto count_compatible = []{ 71 | return (std::is_layout_compatible_v + ...); 72 | }(); 73 | ``` 74 | 75 | > https://godbolt.org/z/GEcr44h49 76 | 77 | ```cpp 78 | template 79 | constexpr auto count_compatible = (... + std::is_layout_compatible_v); 80 | ``` 81 | 82 | > https://godbolt.org/z/fxqYeben5 83 | -------------------------------------------------------------------------------- /tips/316.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * Did you know about `std::rank/std::rank_v` type_trait to get the rank of the array? 4 | 5 | * https://eel.is/c++draft/meta.unary.prop.query#lib:rank 6 | 7 |

Example

8 | 9 | ```cpp 10 | static_assert(0 == std::rank_v); 11 | static_assert(0 == std::rank_v); 12 | static_assert(1 == std::rank_v); 13 | static_assert(0 == std::rank_v); 14 | static_assert(1 == std::rank_v); 15 | static_assert(1 == std::rank_v); 16 | static_assert(2 == std::rank_v); 17 | static_assert(2 == std::rank_v); 18 | static_assert(3 == std::rank_v); 19 | ``` 20 | 21 | > https://godbolt.org/z/e657s3 22 | 23 |

Puzzle

24 | 25 | > Can you implement standard compliant version of `std::rank_v`? 26 | 27 | ```cpp 28 | static_assert(0 == rank_v); 29 | static_assert(0 == rank_v); 30 | static_assert(1 == rank_v); 31 | static_assert(0 == rank_v); 32 | static_assert(1 == rank_v); 33 | static_assert(1 == rank_v); 34 | static_assert(2 == rank_v); 35 | static_assert(2 == rank_v); 36 | static_assert(3 == rank_v); 37 | ``` 38 | 39 | > https://godbolt.org/z/7Ms3W4TdP 40 | 41 |

Solutions

42 | 43 | ```cpp 44 | #include 45 | 46 | template 47 | struct prev; 48 | 49 | template 50 | struct prev : std::type_identity {}; 51 | 52 | template 53 | struct prev : std::type_identity {}; 54 | 55 | template 56 | constexpr auto return_rank() 57 | { 58 | if constexpr (::std::is_array_v) { 59 | return return_rank::type>() + 1; 60 | } 61 | return 0; 62 | } 63 | 64 | template 65 | constexpr auto rank_v = return_rank(); 66 | ``` 67 | 68 | > https://godbolt.org/z/qdx7bxde7 69 | 70 | ```cpp 71 | namespace detail { 72 | template 73 | constexpr auto rank(){ 74 | if constexpr (std::is_array_v) { 75 | return 1 + rank>(); 76 | } else { 77 | return 0; 78 | } 79 | } 80 | }; 81 | 82 | template 83 | constexpr auto rank_v = detail::rank(); 84 | ``` 85 | 86 | > https://godbolt.org/z/c88aheerd 87 | 88 | ```cpp 89 | template 90 | struct rank : public std::integral_constant {}; 91 | 92 | template 93 | struct rank : public std::integral_constant::value + 1> {}; 94 | 95 | template 96 | struct rank : public std::integral_constant::value + 1> {}; 97 | 98 | template< class T > 99 | inline constexpr std::size_t rank_v = rank::value; 100 | 101 | ``` 102 | 103 | > https://godbolt.org/z/YYdTesPKx 104 | 105 | ```cpp 106 | template 107 | constexpr auto rank_v = 0uz; 108 | 109 | template 110 | constexpr auto rank_v = rank_v + 1uz; 111 | 112 | template 113 | constexpr auto rank_v = rank_v; 114 | ``` 115 | 116 | > https://godbolt.org/z/G75xEEGfz 117 | 118 | ```cpp 119 | template 120 | constexpr auto rank_v = []{ 121 | constexpr auto recur = [](auto recur, std::type_identity, auto accum){ 122 | if constexpr (std::is_array_v){ 123 | return recur(recur, std::type_identity>{}, accum+1); 124 | } else { 125 | return accum; 126 | } 127 | }; 128 | return recur(recur, std::type_identity{}, 0); 129 | }(); 130 | ``` 131 | 132 | > https://godbolt.org/z/5eE1MWbj9 133 | -------------------------------------------------------------------------------- /tips/317.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know that with C++20 you can pass concepts?** 4 | 5 | * Although `template concept C>` is not valid 6 | * https://eel.is/c++draft/#concepts 7 | 8 |

Example

9 | 10 | ```cpp 11 | template 12 | concept foo_like = requires(T t) { t.foo; }; 13 | 14 | template 15 | struct foo { 16 | auto fn(auto f) { 17 | static_assert(requires { Concept(f); }); 18 | } 19 | }; 20 | 21 | int main() { 22 | foo<[](foo_like auto){}> f{}; 23 | 24 | struct { int foo{}; } foo; 25 | struct { } bar; 26 | 27 | f.fn(foo); // ok 28 | f.fn(bar); // error: contrain not satisfied 29 | } 30 | ``` 31 | 32 | > https://godbolt.org/z/dE9nWdETs 33 | 34 |

Puzzle

35 | 36 | > **Can you implement simple dependency injection framework based on concepts?** 37 | 38 | ```cpp 39 | template 40 | constexpr auto create(auto&& injector); // TODO 41 | 42 | template concept foo_like = requires(T t) { t.f; }; 43 | template concept bar_like = requires(T t) { t.b; }; 44 | 45 | auto foo_like_v = [](foo_like auto){}; 46 | auto bar_like_v = [](bar_like auto){}; 47 | 48 | struct foo { int f{}; }; 49 | struct bar { int b{}; }; 50 | 51 | struct app { 52 | static constexpr auto ctor_traits = injector{foo_like_v, bar_like_v}; // reflection 53 | app(foo_like auto f, bar_like auto b) { } 54 | }; 55 | 56 | int main() { 57 | auto i = injector{ 58 | bind{}, 59 | bind{}, 60 | }; 61 | 62 | auto a = create(i); 63 | static_assert(sizeof(a)); 64 | } 65 | ``` 66 | 67 | > https://godbolt.org/z/8T9P174M5 68 | 69 |

Solutions

70 | 71 | ```cpp 72 | template 73 | struct tuple_ref_index; 74 | 75 | template 76 | struct tuple_ref_index, I> 77 | : std::conditional::value 78 | , std::integral_constant 79 | , tuple_ref_index, I+1> 80 | >::type 81 | { 82 | }; 83 | 84 | template 85 | auto tuple_ref_by_inheritance(Tuple&& tuple) 86 | -> decltype(std::get::type>::value>(std::forward(tuple))) 87 | { 88 | return std::get::type>::value>(std::forward(tuple)); 89 | } 90 | 91 | template 92 | auto get_bind_type(Bind) 93 | { 94 | return typename Bind::type{}; 95 | } 96 | 97 | template 98 | auto get_bind_types(std::tuple bts, std::tuple) 99 | { 100 | return std::tuple{get_bind_type(tuple_ref_by_inheritance(bts))...}; 101 | } 102 | 103 | template 104 | auto get_types_as_tuple(injector) 105 | { 106 | return std::tuple{}; 107 | } 108 | 109 | template 110 | constexpr auto create(auto&& injector) 111 | { 112 | auto bind_types = get_types_as_tuple(injector); 113 | auto ctor_traits_types = get_types_as_tuple(T::ctor_traits); 114 | 115 | return std::make_from_tuple(get_bind_types(bind_types, ctor_traits_types)); 116 | } 117 | ``` 118 | 119 | > https://godbolt.org/z/zoYjGs3xE 120 | 121 | ```cpp 122 | template 123 | constexpr auto create(auto injector) { 124 | if constexpr (requires { T::ctor_traits; }) { 125 | return [=](::injector) { 126 | return T{[=](bind) { 127 | return create(injector); 128 | }.template operator()(injector)...}; 129 | }(T::ctor_traits); 130 | } else { 131 | return T{}; 132 | } 133 | } 134 | ``` 135 | 136 | > https://godbolt.org/z/beTnhh31r 137 | 138 | ```cpp 139 | template 140 | constexpr auto create(auto&& injector) { 141 | return [&](::injector) { 142 | return T{ (Ts{}, [](bind) { return _{}; }(injector))... }; 143 | }(T::ctor_traits); 144 | } 145 | ``` 146 | 147 | > https://godbolt.org/z/Yq7odx86e 148 | -------------------------------------------------------------------------------- /tips/319.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know that C++11 allows calling functions with reference-to-array parameters from an initializer list?** 4 | 5 | * https://eel.is/c++draft/dcl.init.aggr 6 | 7 |

Example

8 | 9 | ```cpp 10 | consteval auto foo(const auto (&value)[1]) { return value[0]; } 11 | static_assert(42 == foo({42})); 12 | ``` 13 | 14 | > https://godbolt.org/z/hnPM17fK7 15 | 16 |

Puzzle

17 | 18 | > **Can you implement sum_n which sums given list of parameters?** 19 | 20 | ```cpp 21 | // TODO sum_n 22 | 23 | static_assert(1==sum_n({1})); 24 | static_assert(1+2==sum_n({1}, {2})); 25 | static_assert(1+2+3+4==sum_n({1, 2}, {3, 4})); 26 | ``` 27 | 28 | > https://godbolt.org/z/YPfExbzjz 29 | 30 |

Solutions

31 | 32 | ```cpp 33 | consteval auto sum_n(const auto ...x) 34 | { 35 | return (x + ...); 36 | } 37 | 38 | consteval auto sum_n(const auto (&... x)[1]) 39 | { 40 | return sum_n(x[0]...); 41 | } 42 | 43 | consteval auto sum_n(const auto (&... x)[2]) 44 | { 45 | return sum_n(x[1]...) + sum_n(x[0]...); 46 | } 47 | ``` 48 | 49 | > https://godbolt.org/z/1fxxz6KEv 50 | 51 | ``` 52 | template 53 | consteval auto sum_n(const Ts (&...x)[Ns]) { 54 | return (... + std::ranges::fold_left(x, Ts{}, std::plus{})); 55 | } 56 | ``` 57 | 58 | > https://godbolt.org/z/bKdz3EEr1 59 | 60 | ``` 61 | template 62 | constexpr auto sum_n(auto (&&...values)[Ns]) { 63 | auto sum = 0; 64 | return ([&] { 65 | for (auto value : values) { 66 | sum += value; 67 | } 68 | }(), ..., sum); 69 | } 70 | ``` 71 | 72 | > https://godbolt.org/z/x7jn47Geb 73 | 74 | ```cpp 75 | template 76 | consteval auto sum_n(const auto (&...array)[N]) { 77 | auto result = (std::accumulate(array, array + N, 0) + ...); 78 | return result; 79 | } 80 | ``` 81 | 82 | > https://godbolt.org/z/rGG7KM7qa 83 | 84 | ```cpp 85 | template 86 | consteval auto sum_n(const Lists (&...v)[N]) { 87 | return (std::accumulate(v, v+N, 0) + ...); 88 | } 89 | ``` 90 | 91 | > https://godbolt.org/z/9hYTe14ds 92 | -------------------------------------------------------------------------------- /tips/324.md: -------------------------------------------------------------------------------- 1 |

Info

2 | 3 | * **Did you know about `virtual` inheritance in C++?** 4 | 5 | * https://eel.is/c++draft/class.mi#def:base_class,virtual 6 | 7 |

Example

8 | 9 | ```cpp 10 | struct A { 11 | int a{}; 12 | }; 13 | struct B : virtual A {}; 14 | struct C : virtual A {}; 15 | struct D : B, C {}; 16 | 17 | int main() { 18 | D d{}; 19 | d.a = {}; // without virtual -> request for member 'a' is ambiguous 20 | } 21 | ``` 22 | 23 | > https://godbolt.org/z/qG46qW7TG 24 | 25 |

Puzzle

26 | 27 | ```cpp 28 | template 29 | struct interface; // TODO 30 | 31 | template 32 | struct implementation final; // TODO 33 | 34 | #include 35 | 36 | int main() { 37 | using namespace boost::ut; 38 | 39 | "virtual type_list interface empty"_test = [] { 40 | auto impl = implementation<>{}; 41 | const interface<>& i = impl; 42 | }; 43 | 44 | "virtual type_list interface single"_test = [] { 45 | auto impl = implementation{}; 46 | const interface& i = impl; 47 | expect(42 == i.on(int{})); 48 | }; 49 | 50 | "virtual type_list interface multi"_test = [] { 51 | auto impl = implementation{}; 52 | const interface& i = impl; 53 | expect(4 == i.on(int{})); 54 | expect(2. == i.on(double{})); 55 | expect(42.f == i.on(float{})); 56 | }; 57 | } 58 | ``` 59 | 60 | > https://godbolt.org/z/qs4K917ac 61 | 62 |

Solutions

63 | 64 | ```cpp 65 | template 66 | struct interface { 67 | public: 68 | template 69 | const T& on(T) const { 70 | return std::get(vs); 71 | } 72 | 73 | protected: 74 | constexpr explicit interface(auto&&... args) : vs(args...) {} 75 | 76 | private: 77 | std::tuple vs; 78 | }; 79 | 80 | template 81 | struct implementation final : interface { 82 | constexpr explicit implementation() : interface(Vs...) {} 83 | }; 84 | ``` 85 | 86 | > https://godbolt.org/z/csrTfEcnP 87 | 88 | ```cpp 89 | template 90 | struct interface : virtual interface... { 91 | using interface::on...; 92 | }; 93 | 94 | template 95 | struct interface { 96 | virtual T on(T) const = 0; 97 | virtual ~interface() noexcept = default; 98 | }; 99 | 100 | template 101 | struct implementation : interface, implementation... {}; 102 | 103 | template 104 | struct implementation : virtual interface { 105 | using T = decltype(V); 106 | T on(T) const { return V; } 107 | }; 108 | ``` 109 | 110 | > https://godbolt.org/z/vo18fjTja 111 | 112 | ```cpp 113 | template 114 | struct interface : virtual interface... { 115 | using interface::on...; 116 | }; 117 | 118 | template 119 | struct interface { 120 | virtual T on(T) const = 0; 121 | }; 122 | 123 | template 124 | struct implementation final : implementation..., 125 | interface {}; 126 | 127 | template 128 | struct implementation : virtual interface { 129 | using T = decltype(V); 130 | T on(T) const override { return V; } 131 | }; 132 | ``` 133 | 134 | > https://godbolt.org/z/bfxPc3KE8 135 | 136 |

137 | -------------------------------------------------------------------------------- /tips/328.md: -------------------------------------------------------------------------------- 1 |
Info

2 | 3 | * **Did you know that C++23 extended floating-point types?** 4 | 5 | * https://wg21.link/P1467R9 6 | 7 |

Example

8 | 9 | ```cpp 10 | #include 11 | 12 | int main() { 13 | std::float16_t f16 = .42f16; 14 | std::bfloat16_t bf16 = .42bf16; 15 | std::float32_t f32 = .42f32; 16 | std::float64_t f64 = .42f64; 17 | std::float128_t f128 = .42f128; 18 | } 19 | ``` 20 | 21 | > https://godbolt.org/z/qrE4v1eno 22 | 23 |

Puzzle

24 | 25 | * **Can you implement `min_max` which returns a tuple with min and max element based on given predicate?** 26 | 27 | ```cpp 28 | int main() { 29 | static_assert( 30 | std::tuple{sizeof(.1f16), sizeof(.1f64)} == min_max<[] { return sizeof(T); }>( 31 | std::tuple{.1f16, .1f64}) 32 | ); 33 | 34 | static_assert( 35 | std::tuple{sizeof(.1f16), sizeof(.1f128)} == min_max<[] { return sizeof(T); }>( 36 | std::tuple{.1f16, .1f32, .1f64, .1f128}) 37 | ); 38 | 39 | static_assert( 40 | std::tuple{sizeof(.1f32), sizeof(.1f128)} == min_max<[] { return sizeof(T); }>( 41 | std::tuple{.1f128, .1f32, .1f32, .1f64}) 42 | ); 43 | } 44 | ``` 45 | 46 | > https://godbolt.org/z/59z431vWc 47 | 48 |

Solutions

49 | 50 | ```cpp 51 | template 52 | constexpr auto min_max(std::tuple&& tuple) 53 | -> std::tuple { 54 | using tuple_t = typename std::remove_cvref_t; 55 | std::array> buffer = {}; 56 | [](auto&& tuple, auto& out, 57 | std::index_sequence indicies) { 58 | (static_cast( 59 | out[Is] = 60 | Func.template operator()>()), 61 | ...); 62 | }(std::forward(tuple), buffer, 63 | std::make_index_sequence>{}); 64 | const auto [min, max] = std::ranges::minmax(buffer); 65 | return {min, max}; 66 | } 67 | ``` 68 | 69 | > https://godbolt.org/z/E36Eh4fqz 70 | 71 | ```cpp 72 | template 73 | constexpr auto min_max(std::tuple &&) 74 | -> std::tuple { 75 | return { 76 | std::min({F.template operator()()...}), 77 | std::max({F.template operator()()...}), 78 | }; 79 | } 80 | ``` 81 | 82 | > https://godbolt.org/z/jb163dP48 83 | 84 | ```cpp 85 | template 86 | constexpr auto min_max(std::tuple&&) -> std::tuple { 87 | auto [mi, mx] = std::minmax({Pred.template operator()()...}); 88 | 89 | return std::make_tuple(mi, mx); 90 | } 91 | ``` 92 | 93 | > https://godbolt.org/z/Ph8vx3xq1 94 | 95 | ```cpp 96 | template 97 | constexpr auto min_max(std::tuple&&) { 98 | return std::tuple{std::minmax({Fn.template operator()()...})}; 99 | } 100 | ``` 101 | 102 | > https://godbolt.org/z/4baj4jeTT 103 | 104 | ```cpp 105 | template 106 | constexpr std::tuple min_max( 107 | std::tuple&& tuple) { 108 | return std::minmax({predicate.template operator()()...}); 109 | } 110 | ``` 111 | 112 | > https://godbolt.org/z/aand176rb 113 | 114 | ```cpp 115 | template 116 | constexpr std::tuple min_max(const std::tuple&) { 117 | return std::minmax({pred.template operator()()...}); 118 | } 119 | ``` 120 | 121 | > https://godbolt.org/z/zxYj97dn7 122 | 123 | ```cpp 124 | template 125 | auto min_max = [](auto t) { 126 | const auto values = std::apply( 127 | [&](F&... floats) { 128 | return std::make_tuple(P.template operator()()...); 129 | }, 130 | t); 131 | const auto min = std::apply( 132 | [](auto&&... args) { return (std::min({args...})); }, values); 133 | const auto max = std::apply( 134 | [](auto&&... args) { return (std::max({args...})); }, values); 135 | return std::tuple(min, max); 136 | }; 137 | ``` 138 | 139 | > https://godbolt.org/z/6zq8hT4YE 140 | 141 |

142 | -------------------------------------------------------------------------------- /tips/331.md: -------------------------------------------------------------------------------- 1 |
Info

2 | 3 | * **Did you about C++17 std::index_sequence, std::make_index_sequence?** 4 | 5 | * https://eel.is/c++draft/utility.syn#lib:index_sequence 6 | 7 |

Example

8 | 9 | ```cpp 10 | template struct whats_my_type; 11 | int main() { whats_my_type>{}; } // error: implicit instantiation of undefined template ‘whats_my_type:8:3: error: implicit instantiation of undefined template ‘whats_my_type’ 12 | ``` 13 | 14 | > https://godbolt.org/z/bj88jMWfW 15 | 16 |

Puzzle

17 | 18 | * **Can you generate - at compile time - a matrix (3x3) with values { true: if T.bar(id) is available, false: otherwise }**? 19 | 20 | ```cpp 21 | template struct id {}; 22 | 23 | struct Foo { 24 | constexpr auto bar(id<0, 0>) {} 25 | constexpr auto bar(id<1, 1>) {} 26 | constexpr auto bar(id<2, 2>) {} 27 | }; 28 | 29 | template 30 | constexpr const auto matrix; // TODO 31 | // matrix: 32 | // .long 1 # 0x1 // because Foo has bar(id<0, 0>) 33 | // .long 0 # 0x0 34 | // .long 0 # 0x0 35 | // .long 0 # 0x0 36 | // .long 1 # 0x1 // because Foo has bar(id<1, 1>) 37 | // .long 0 # 0x0 38 | // .long 0 # 0x0 39 | // .long 0 # 0x0 40 | // .long 1 # 0x1 // because Foo has bar(id<2, 2>) 41 | 42 | static_assert(matrix[0][0]); 43 | static_assert(not matrix[0][1]); 44 | static_assert(not matrix[0][2]); 45 | static_assert(not matrix[0][1]); 46 | static_assert(matrix[1][1]); 47 | static_assert(not matrix[2][1]); 48 | static_assert(not matrix[0][2]); 49 | static_assert(not matrix[1][2]); 50 | static_assert(matrix[2][2]); 51 | ``` 52 | 53 | > https://godbolt.org/z/3YvfqezPa 54 | 55 |

Solutions

56 | 57 | ```cpp 58 | template > 59 | constexpr const std::array, N> matrix{}; 60 | 61 | template 62 | constexpr const std::array matrix>{ 63 | [] -> std::array { 64 | return {[] -> int { 65 | return requires(T v, id p) { v.bar(p); }; 66 | }.template operator()()...}; 67 | }.template operator()()...}; 68 | ``` 69 | 70 | > https://godbolt.org/z/Yq9xdYo3a 71 | 72 | ```cpp 73 | template 74 | constexpr auto has = []() { return requires(T t) { t.bar(id{}); }; }; 75 | 76 | template 77 | constexpr auto row = []() { 78 | constexpr auto make_row = 79 | [](std::index_sequence) -> std::array { 80 | return {has()...}; 81 | }; 82 | 83 | return make_row(std::make_index_sequence{}); 84 | }; 85 | 86 | template 87 | constexpr auto rows = []() { 88 | constexpr auto make_rows = [](std::index_sequence) 89 | -> std::array, N> { return {row()...}; }; 90 | 91 | return make_rows(std::make_index_sequence{}); 92 | }; 93 | 94 | template 95 | constexpr const auto matrix = rows(); 96 | ``` 97 | 98 | > https://godbolt.org/z/Gcx4sd5E3 99 | 100 | ```cpp 101 | template 102 | concept hasBar = requires(T t) { 103 | { t.bar(std::declval>()) }; 104 | }; 105 | 106 | template 107 | constexpr std::array, N> make_symmetric_matrix( 108 | Args&&... args) { 109 | return {std::forward(args)...}; 110 | } 111 | 112 | template 113 | constexpr const auto matrix = 114 | [](std::index_sequence) 115 | -> std::array, N> { 116 | return make_symmetric_matrix(hasBar(Indices)>...); 117 | }(std::make_index_sequence{}); 118 | ``` 119 | 120 | > https://godbolt.org/z/bdjEadn5f 121 | 122 |

123 | -------------------------------------------------------------------------------- /tips/332.md: -------------------------------------------------------------------------------- 1 |
Info

2 | 3 | * **Did you know that in C++ you can generate jump tables at compile-time?** 4 | 5 | * https://en.wikipedia.org/wiki/Branch_table 6 | 7 |

Example

8 | 9 | ```cpp 10 | template constexpr auto foo() { return N; } 11 | 12 | constexpr std::array jump_table{ 13 | foo<0>, 14 | foo<1>, 15 | foo<2>, 16 | }; 17 | 18 | static_assert(0 == jump_table[0]()); 19 | static_assert(1 == jump_table[1]()); 20 | static_assert(2 == jump_table[2]()); 21 | ``` 22 | 23 | > https://godbolt.org/z/x3xa9erGE 24 | 25 |

Puzzle

26 | 27 | * **Can you implemnt dispatch fn which generates jump table for given N?** 28 | 29 | ```cpp 30 | template constexpr auto foo() { return N; } 31 | 32 | template 33 | constexpr auto dispatch(auto n); // TODO 34 | 35 | static_assert(1 == dispatch(1)); 36 | static_assert(7 == dispatch(7)); 37 | static_assert(23 == dispatch(23)); 38 | ``` 39 | 40 | > https://godbolt.org/z/4M9x1vjcG 41 | 42 |

Solutions

43 | 44 | ```cpp 45 | template 46 | constexpr auto dispatch(auto n) -> int { 47 | using foo_return_type = std::invoke_result)>::type; 48 | const auto jump_table = [](std::index_sequence) { 49 | return std::array{(foo())...}; 50 | }(std::make_index_sequence{}); 51 | return jump_table[n]; 52 | }; 53 | ``` 54 | 55 | > https://godbolt.org/z/PW3qvrnf6 56 | 57 |

58 | -------------------------------------------------------------------------------- /tips/333.md: -------------------------------------------------------------------------------- 1 |
Info

2 | 3 | * Did you know that C++20 added std::span? 4 | 5 | * https://eel.is/c++draft/views.span#span.elem 6 | 7 |

Example

8 | 9 | ```cpp 10 | #include 11 | 12 | constexpr std::array a = {1, 2, 3, 4, 5}; 13 | constexpr std::span s{a}; 14 | 15 | static_assert(s[0]==a[0]); 16 | ``` 17 | 18 | > https://godbolt.org/z/T76csW9MM 19 | 20 |

Puzzle

21 | 22 | * **Can you implement sum using std algorithms which takes the span?** 23 | 24 | * Double points for finding multiple algorithms to achieve it 25 | 26 | ```cpp 27 | constexpr std::array a = {1, 2, 3, 4, 5}; 28 | constexpr std::span s{a}; 29 | 30 | //TODO sum 31 | 32 | static_assert(15 == sum(s)); 33 | ``` 34 | 35 | > https://godbolt.org/z/4hYExTxdq 36 | 37 |

Solutions

38 | 39 | ```cpp 40 | constexpr std::array a = {1, 2, 3, 4, 5}; 41 | constexpr std::span s{a}; 42 | 43 | constexpr auto sum = []([[maybe_unused]] auto s) { 44 | return std::reduce(s.begin(), s.end(), 0); 45 | }; 46 | 47 | static_assert(15 == sum(s)); 48 | ``` 49 | 50 | > https://godbolt.org/z/zff6qPcT5 51 | 52 | ```cpp 53 | constexpr std::array a = {1, 2, 3, 4, 5}; 54 | constexpr std::span s{a}; 55 | 56 | constexpr auto sum = []([[maybe_unused]] auto s) { 57 | return std::accumulate(std::cbegin(s), std::cend(s), 0); 58 | }; 59 | 60 | static_assert(15 == sum(s)); 61 | ``` 62 | 63 | > https://godbolt.org/z/1dx51dWeq 64 | 65 |

66 | -------------------------------------------------------------------------------- /tips/334.md: -------------------------------------------------------------------------------- 1 |
Info

2 | 3 | * **Did you know that C++23 added std::invoke_r?** 4 | 5 | * https://wg21.link/P2136 6 | 7 |

Example

8 | 9 | ```cpp 10 | #include 11 | 12 | constexpr auto sum(auto... ts) { return (ts + ...); } 13 | static_assert(typeid(int) == typeid(std::invoke_r(&sum, 3, 4))); 14 | ``` 15 | 16 | > https://godbolt.org/z/1Wd6Ms3Wa 17 | 18 |

Puzzle

19 | 20 | * **Can you implement fn `call` which invokes fn `sum` with given parameters `ts...` via `invoke_r` and returns a `common_type` of `ts...` sum?** 21 | 22 | ```cpp 23 | constexpr auto sum(auto... ts) { return (ts + ...); } 24 | 25 | [[nodiscard]] constexpr auto call(auto... ts); // TODO 26 | 27 | static_assert(6 == call(1, 2, 3)); 28 | static_assert(typeid(int) == typeid(call(1, 2, 3))); 29 | 30 | static_assert(6u == call(1u, 2u, 3u)); 31 | static_assert(typeid(unsigned) == typeid(call(1u, 2u, 3u))); 32 | 33 | static_assert(6u == call(1, 2u, 3l)); 34 | static_assert(typeid(long) == typeid(call(1, 2u, 3l))); 35 | 36 | static_assert(6u == call(1ll, 2ull, 3l)); 37 | static_assert(typeid(unsigned long long) == typeid(call(1ll, 2ull, 3l))); 38 | ``` 39 | 40 | > https://godbolt.org/z/3WP5YG8dv 41 | 42 |

Solutions

43 | 44 | ```cpp 45 | [[nodiscard]] constexpr auto call(auto... ts) { 46 | using Ret = std::common_type_t; 47 | return std::invoke_r(sum, ts...); 48 | } 49 | ``` 50 | 51 | > https://godbolt.org/z/1baMcc331 52 | 53 | ```cpp 54 | [[nodiscard]] constexpr auto call(auto... ts){ 55 | using T = typename std::common_type_t; 56 | return std::invoke_r(&sum, ts...); 57 | } 58 | ``` 59 | 60 | > https://godbolt.org/z/xcn9qnrcY 61 | 62 |

63 | -------------------------------------------------------------------------------- /tips/336.md: -------------------------------------------------------------------------------- 1 |
Info

2 | 3 | * **Did you know about `gnu::vector_size` extension?** 4 | 5 | * https://gcc.gnu.org/onlinedocs/gcc/Vector-Extensions.html 6 | 7 |

Example

8 | 9 | ```cpp 10 | int main() { 11 | using v4si = int [[gnu::vector_size(4 * sizeof(int))]]; 12 | 13 | v4si a = {1, 2, 3, 4}; 14 | v4si b = {4, 3, 2, 1}; 15 | v4si c; 16 | 17 | c = a + b; 18 | std::cout << c[0] << c[1] << c[2] << c[3]; // prints 5555 19 | } 20 | ``` 21 | 22 | > https://godbolt.org/z/fTMsK8Moh 23 | 24 |

Puzzle

25 | 26 | * Can you implement `matmul` - vectorized matrix multiplication with `gnu::vector_size`? 27 | 28 | ```cpp 29 | using v16qi = std::int8_t [[gnu::vector_size(16 * sizeof(std::int8_t))]]; 30 | 31 | template 32 | constexpr auto matmul(v16qi (&A)[N], v16qi (&B)[N], std::int32_t (&C)[N][N]) { 33 | // TODO 34 | } 35 | 36 | int main() { 37 | using namespace boost::ut; 38 | 39 | "matmul"_test = [] { 40 | v16qi A[4] = {{1, 2, 3, 4}, 41 | {5, 6, 7, 8}, 42 | {9, 10, 11, 12}, 43 | {13, 14, 15, 16}}; 44 | v16qi B[4] = {{16, 15, 14, 13}, 45 | {12, 11, 10, 9}, 46 | {8, 7, 6, 5}, 47 | {4, 3, 2, 1}}; 48 | std::int32_t C[4][4] = {0}; 49 | 50 | matmul(A, B, C); 51 | 52 | expect(C[0][0] = 80 and C[0][3] == 50 and C[3][0] == 560 and C[3][3] == 386); 53 | }; 54 | } 55 | ``` 56 | 57 | > https://godbolt.org/z/eGj3eddKM 58 | 59 |

60 | 61 |

Solutions

62 | 63 | ```cpp 64 | template 65 | constexpr auto matmul(v16qi (&A)[N], v16qi (&B)[N], std::int32_t (&C)[N][N]) { 66 | static_assert(N <= 16, "Matrices larger than 16x16 are not supported"); 67 | auto sum = [](const auto &vec) { 68 | auto ret = [&vec](std::index_sequence) { 69 | return (vec[Idxs] + ...); 70 | }(std::make_index_sequence{}); 71 | return ret; 72 | }; 73 | using v64si = std::int32_t [[gnu::vector_size(16 * sizeof(std::int32_t))]]; 74 | auto get_col = [&B](const auto col_idx) { 75 | auto col = 76 | [&B, col_idx](std::index_sequence) { 77 | return v16qi{B[Idxs][col_idx]...}; 78 | }(std::make_index_sequence{}); 79 | return __builtin_convertvector(col, v64si); 80 | }; 81 | for (auto i = 0; i < N; ++i) { 82 | for (auto j = 0; j < N; ++j) { 83 | C[i][j] = sum(__builtin_convertvector(A[i], v64si) * get_col(j)); 84 | } 85 | } 86 | } 87 | ``` 88 | 89 | > https://godbolt.org/z/earr7KxK4 90 | 91 |

92 | -------------------------------------------------------------------------------- /tips/337.md: -------------------------------------------------------------------------------- 1 |
Info

2 | 3 | * **Did you know that run-time dispatching over type-list can be implemented many different ways?** 4 | 5 |

Example

6 | 7 | ```cpp 8 | template