diff --git a/userland/cpp/initializer_list_constructor.cpp b/userland/cpp/initializer_list_constructor.cpp new file mode 100644 index 0000000..74661f9 --- /dev/null +++ b/userland/cpp/initializer_list_constructor.cpp @@ -0,0 +1,105 @@ +// https://cirosantilli.com/linux-kernel-module-cheat#cpp +// +// # Brace enclosed initializer list +// +// # List initialization +// +// # Initializer list constructor +// +// Applications: +// +// - you don't know beforehand how many arguments a constructor should receive +// +// For example, the stdlib std::vector class gets an initializer list constructor on C++11, +// which allows one to initialize it to any constant. +// +// TODO could this not be achieved via cstdarg? + +#include +#include +#include + +int main() { +#if __cplusplus >= 201103L + // STL std::vector usage example + { + std::vector v{0, 1}; + // SAME. + //std::vector v = std::vector({0, 1}); + assert(v[0] == 0); + assert(v[1] == 1); + assert(v == std::vector({0, 1})); + assert((v == std::vector{0, 1})); + + // Assignment also works via implicit conversion. + v = {1, 0}; + assert((v == std::vector{1, 0})); + + // ERROR: TODO why no implicit conversion is made? + //assert((v == {0, 1})); + } + + // How to implement one yourself. + { + struct InitializerListCtor { + std::vector v; + + InitializerListCtor(int i, int j) { + v.push_back(i); + v.push_back(j + 1); + } + + InitializerListCtor(std::initializer_list list) { + for (auto& i : list) + v.push_back(i); + } + + InitializerListCtor(int before, std::initializer_list list, int after) { + v.push_back(before + 1); + for (auto& i : list) + v.push_back(i); + v.push_back(after - 1); + } + }; + + // Initializer list constructor is called, not the (int,int) one. + { + InitializerListCtor o{0, 1}; + assert((o.v == std::vector{0, 1})); + } + + // 3 param constructor is called + { + InitializerListCtor o(0, {0, 0,}, 0); + assert((o.v == std::vector{1, 0, 0, -1})); + } + } + + // # auto and initializer lists + // + // auto rule: brace initializer can be bound to auto + // + // http://en.cppreference.com/w/cpp/utility/initializer_list + // + { + { + // TODO GCC 5.1 does not allow this, which conflicts with + // http://en.cppreference.com/w/cpp/utility/initializer_list + // Who is right? + // auto InitializerListCtor{0, 1, 2}; + // SAME: + //initializer_list l{0, 1, 2}; + //assert(l.size() == 3); + //assert(*l.begin() == 0); + } + + // The rule for auto makes this ranged for work. + // TODO why here? I see an `int`, not an `auto` + int i = 0; + for (auto x : {0, 1, 2}) { + assert(x == i); + i++; + } + } +#endif +} diff --git a/userland/cpp/most_vexing_parse.cpp b/userland/cpp/most_vexing_parse.cpp index 087688e..c9d30ac 100644 --- a/userland/cpp/most_vexing_parse.cpp +++ b/userland/cpp/most_vexing_parse.cpp @@ -1,17 +1,20 @@ // https://cirosantilli.com/linux-kernel-module-cheat#cpp -#include "common.hpp" +#include int main() { + struct D { + int i; + D() {} + D(int i) : i(i) {} + }; + struct C { int i; C() : i(1) {} C(int i) : i(i) {} - }; - - struct D { - D() {} + C(const D& d) : i(d.i) {} }; // Declares *FUNCTION* called `c` that returns `C` inside function main. @@ -46,12 +49,36 @@ int main() { } // But sometimes even arguments are not enough: here D() - // could matn that the declared `c` + // is interpreted as "a function of type `D f()`" { - C c(D()); + C c(D(2)); #if 0 // error: request for member ‘i’ in ‘c’, which is of non-class type ‘main()::C(main()::D (*)())’ assert(c.i == 2); #endif } + + // Solving the most vexing parse. + // https://stackoverflow.com/questions/13249694/avoid-the-most-vexing-parse + { + // Extra parenthesis. + { + C c((D(2))); + assert(c.i == 2); + } + + // Initialize through assignment. TODO likely guaranteed to be cost-free, + // but confirm. + { + C c = C((D(2))); + assert(c.i == 2); + } + + // Initializer list. Only works if there is no initializer_list constructor. + // Only works in general if c does not have an ambiguous initializer_list constructor though. + { + C c{D(2)}; + assert(c.i == 2); + } + } }