diff --git a/1-ProceduralProgramming/7-StaticAndConstexpr.ipynb b/1-ProceduralProgramming/7-StaticAndConstexpr.ipynb index 24bd3c71df17ec4c47ca52b3f272e1f6bf8d7428..a38706a92f64bb0e8ef1aef6ab6f58d3efac11b1 100644 --- a/1-ProceduralProgramming/7-StaticAndConstexpr.ipynb +++ b/1-ProceduralProgramming/7-StaticAndConstexpr.ipynb @@ -108,7 +108,7 @@ "source": [ "## Constexpr\n", "\n", - "Let's consider the STL container `std::array`, which is the container of choice to store an array which size is known *at compile time* (containers will be seen more in depth in a [future notebook](./notebooks/5-UsefulConceptsAndSTL/3-Containers.ipynb))." + "We've seen the allocation of an array on the stack follows this syntax:" ] }, { @@ -117,15 +117,15 @@ "metadata": {}, "outputs": [], "source": [ - "#include <array>\n", - "\n", - "std::array<double, 3ul> my_array;" + "int array[5ul];" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ + "where the size of the array is available at compile time (if not you have to use an array allocated on the heap at runtime).\n", + "\n", "Now imagine we want to init such an array with a size that results from a computation; let's say a Fibonacci series:" ] }, @@ -165,8 +165,7 @@ "metadata": {}, "outputs": [], "source": [ - "#include <array>\n", - "std::array<double, fibonacci(4)> my_fibonacci_4_array; // COMPILATION ERROR!" + "double array[fibonacci(5)]; // COMPILATION ERROR!" ] }, { @@ -178,7 +177,7 @@ "In C++ 03, you had two choices to resolve this\n", "\n", "- Using a macro...\n", - "- Or using [metaprogramming](./4-Templates/4-Metaprogramming.ipynb) which is a tad overkill... (and involves boilerplate code).\n", + "- Or using [metaprogramming](../4-Templates/4-Metaprogramming.ipynb) which is a tad overkill... (and involves boilerplate code).\n", "\n", "C++ introduced the keyword `constexpr`, which indicates something happens at compile time. It might be used for just a variable, or for more complicated construct such as functions or classes. The only requirement is that all the subparts used are themselves `constexpr`." ] @@ -206,9 +205,7 @@ "metadata": {}, "outputs": [], "source": [ - "#include <array>\n", - "constexpr auto fibonacci_index = 4ul;\n", - "std::array<double, fibonacci_const(fibonacci_index)> my_fibonacci_4_array; " + "double array[fibonacci_const(5)]; // Ok!" ] }, { @@ -228,8 +225,7 @@ "\n", "int i = 7;\n", "++i; // i is by no stretch a compile time variable!\n", - "\n", - "std::cout << fibonacci_const(7) << std::endl; // ok!" + "std::cout << fibonacci_const(7) << std::endl;" ] }, { @@ -239,7 +235,9 @@ "`constexpr` becomes increasingly powerful over time:\n", "\n", "- The function `fibonacci_const` above does not in fact work before C++ 14: this version of the standard introduced the possibility to provide several `return` in a `constexpr` function.\n", - "- `constexpr` is added wherever possible in the STL. This is still a work in progress: if you look for instance the [CppReference page](https://en.cppreference.com/w/cpp/algorithm/count) for `std::count` algorithm, you will see this algorithm becomes `constexpr` in C++ 20." + "- `constexpr` is added wherever possible in the STL. This is still a work in progress: if you look for instance the [CppReference page](https://en.cppreference.com/w/cpp/algorithm/count) for `std::count` algorithm, you will see this algorithm becomes `constexpr` in C++ 20.\n", + "\n", + "We will see another use of `constexpr` in a [later notebook](../4-Templates/2-Specialization.ipynb#If-constexpr)." ] }, { diff --git a/4-Templates/4-Metaprogramming.ipynb b/4-Templates/4-Metaprogramming.ipynb index 5c426acc4df70ea05958e12cd5eceef914f3f01a..6add317dcb177fa222413f775059c3ceaff28793 100644 --- a/4-Templates/4-Metaprogramming.ipynb +++ b/4-Templates/4-Metaprogramming.ipynb @@ -209,6 +209,142 @@ "}" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Bonus: metaprogramming Fibonacci\n", + "\n", + "In [notebook about constexpr](../1-ProceduralProgramming/7-StaticAndConstexpr.ipynb), I said implementing Fibonacci series before C++ 11 involved metaprogramming; here is an implementation (much more wordy than the `constexpr` one):\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "template<std::size_t N>\n", + "struct Fibonacci\n", + "{\n", + " \n", + " static std::size_t Do()\n", + " {\n", + " return Fibonacci<N-1>::Do() + Fibonacci<N-2>::Do();\n", + " }\n", + " \n", + " \n", + "};" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "// Don't forget the specialization for 0 and 1!\n", + "\n", + "template<>\n", + "struct Fibonacci<0ul>\n", + "{\n", + " \n", + " static std::size_t Do()\n", + " {\n", + " return 0ul;\n", + " }\n", + " \n", + " \n", + "};\n", + "\n", + "\n", + "template<>\n", + "struct Fibonacci<1ul>\n", + "{\n", + " \n", + " static std::size_t Do()\n", + " {\n", + " return 1ul;\n", + " }\n", + " \n", + " \n", + "};" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <iostream>\n", + "\n", + "std::cout << Fibonacci<5ul>::Do() << std::endl;\n", + "std::cout << Fibonacci<10ul>::Do() << std::endl;" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And if the syntax doesn't suit you... you could always add an extra level of indirection to remove the `::Do()` part:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "template<std::size_t N>\n", + "std::size_t FibonacciWrapper()\n", + "{\n", + " return Fibonacci<N>::Do();\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <iostream>\n", + "\n", + "std::cout << FibonacciWrapper<5ul>() << std::endl;\n", + "std::cout << FibonacciWrapper<10ul>() << std::endl;\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you can see, in some cases `constexpr` really alleviates some tedious boilerplate... \n", + "\n", + "It should be noticed that although these computations really occur at compile time, they aren't nonetheless recognized automatically as `constexpr`: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "constexpr auto fibo_5 = FibonacciWrapper<5ul>(); // COMPILATION ERROR!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To fix that, you need to declare `constexpr`:\n", + "- Each of the `Do` static method (`static constexpr std::size_t Do()`)\n", + "- The `FibonacciWrapper` function (`template<std::size_t N> constexpr std::size_t FibonacciWrapper()`)\n", + "\n", + "So in this specific case you should really go with the much less wordy and more expressive expression with `constexpr` given in [aforementioned notebook](../1-ProceduralProgramming/7-StaticAndConstexpr.ipynb)" + ] + }, { "cell_type": "markdown", "metadata": {},