learn more c++, it never ends

This commit is contained in:
Ciro Santilli 六四事件 法轮功
2020-03-19 00:00:00 +00:00
parent 082166a360
commit d09a0d97b8
6 changed files with 277 additions and 29 deletions

View File

@@ -15496,7 +15496,7 @@ Programs under link:userland/cpp/[] are examples of https://en.wikipedia.org/wik
* templates * templates
** link:userland/cpp/template.cpp[]: basic example ** link:userland/cpp/template.cpp[]: basic example
** link:userland/cpp/template_class_with_static_member.cpp[]: https://stackoverflow.com/questions/3229883/static-member-initialization-in-a-class-template ** link:userland/cpp/template_class_with_static_member.cpp[]: https://stackoverflow.com/questions/3229883/static-member-initialization-in-a-class-template
** link:userland/cpp/if_constexpr.cpp[]: C++17 `if constexpr` ** link:userland/cpp/if_constexpr.cpp[]: C++17 `if constexpr`: https://stackoverflow.com/questions/12160765/if-else-at-compile-time-in-c/54647315#54647315
* iostream * iostream
** link:userland/cpp/copyfmt.cpp[]: `std::copyfmt` restores stream state, see also: https://stackoverflow.com/questions/12560291/set-back-default-floating-point-print-precision-in-c/53673686#53673686 ** link:userland/cpp/copyfmt.cpp[]: `std::copyfmt` restores stream state, see also: https://stackoverflow.com/questions/12560291/set-back-default-floating-point-print-precision-in-c/53673686#53673686
* fstream * fstream
@@ -15509,6 +15509,26 @@ Programs under link:userland/cpp/[] are examples of https://en.wikipedia.org/wik
*** <<algorithms>> contains a benchmark comparison of different c++ containers *** <<algorithms>> contains a benchmark comparison of different c++ containers
*** link:userland/cpp/set.cpp[]: `std::set` contains unique keys *** link:userland/cpp/set.cpp[]: `std::set` contains unique keys
[[cpp-initialization-types]]
==== C++ initialization types
OMG this is hell, understand when primitive variables are initialized or not:
* https://stackoverflow.com/questions/3127454/how-do-c-class-members-get-initialized-if-i-dont-do-it-explicitly
* https://blog.tartanllama.xyz/initialization-is-bonkers/
Intuition:
* direct initialization: a constructor called explicitly with at least one argument: https://en.cppreference.com/w/cpp/language/direct_initialization
* default initialization: does not initialize primitive types: https://en.cppreference.com/w/cpp/language/default_initialization
* value initialization: maybe initializes primitive types: https://en.cppreference.com/w/cpp/language/value_initialization
* zero initialization: initializes primitive types
Good rule:
* initialize every single variable explicitly to prevent the risk of having uninitialized variables due to programmer error (which is easy to get wrong due to insane rules)
* if you don't define your own default constructor, always `= delete` it instead. This prevents the possibility that variables will be assigned twice due to zero initialization
[[cpp-multithreading]] [[cpp-multithreading]]
==== C++ multithreading ==== C++ multithreading

View File

@@ -670,6 +670,8 @@ path_properties_tuples = (
}, },
), ),
'count.cpp': {'more_than_1s': True}, 'count.cpp': {'more_than_1s': True},
'initialization_types.cpp': {'cc_flags':
['-Wno-unused-variable', LF, '-Wno-unused-but-set-variable', LF]},
'm5ops.cpp': {'allowed_emulators': {'gem5'}}, 'm5ops.cpp': {'allowed_emulators': {'gem5'}},
'parallel_sort.cpp': {'minimum_gcc_version': (9, 0, 0)}, 'parallel_sort.cpp': {'minimum_gcc_version': (9, 0, 0)},
'sleep_for.cpp': { 'sleep_for.cpp': {

View File

@@ -0,0 +1,22 @@
// https://cirosantilli.com/linux-kernel-module-cheat#cpp
constexpr int f() {
int i = 0;
while (1)
i += 1;
return i;
}
constexpr int g() {
return g();
}
int main() {
#if 0
// GCC 9.2.1. error: constexpr loop iteration count exceeds limit of 262144 (use -fconstexpr-loop-limit= to increase the limit)
static_assert(f() == 0);
// GCC 9.2.1. error: constexpr evaluation depth exceeds maximum of 512 (use -fconstexpr-depth= to increase the maximum)
static_assert(g() == 0);
#endif
}

View File

@@ -1,23 +1,30 @@
// https://cirosantilli.com/linux-kernel-module-cheat#cpp // https://cirosantilli.com/linux-kernel-module-cheat#cpp
#if __cplusplus >= 201703L #if __cplusplus >= 201703L
#include <cassert> #include <cassert>
#include <type_traits> #include <type_traits>
template <class T> template<typename T>
struct MyClass { struct MyClass {
int myFunc() { MyClass() : myVar{0} {}
if constexpr(std::is_integral<T>()) void modifyIfNotConst() {
return 1; if constexpr(!isconst) {
else myVar = 1;
return 2; }
} }
T myVar;
static constexpr bool isconst = std::is_const<T>::value;
}; };
#endif #endif
int main() { int main() {
#if __cplusplus >= 201703L #if __cplusplus >= 201703L
assert(MyClass<int>().myFunc() == 1); MyClass<double> x;
assert(MyClass<float>().myFunc() == 2); MyClass<const double> y;
x.modifyIfNotConst();
y.modifyIfNotConst();
assert(x.myVar == 1);
assert(y.myVar == 0);
#endif #endif
} }

View File

@@ -0,0 +1,199 @@
// https://cirosantilli.com/linux-kernel-module-cheat#cpp-initialization-types
#include <cassert>
#include <type_traits>
// zero-initialization because has static storage
// just like a static "local" function variable.
int global;
int main() {
// First let's list how different initializations are done explicitly in the code.
{
struct C {
int i;
constexpr C() : i(0) {}
constexpr C(int i) : i(i) {}
};
// Default-initialization.
{
C i;
C *j = new C;
}
// Value initialization.
{
C i{};
C j = C();
C k = C{};
C *l = new C();
C *m = new C{};
}
// Direct initialization.
{
C i(1);
C j{1};
C *k = new C(1);
}
// Zero initialization
{
static C i;
}
// Most vexing parse, function declaration!
{
C myfunc();
}
// The syntax goes for primitive types. These serve as the basis
// for the recursive definition..
{
// Default.
int i;
// Following the cases at:
// https://en.cppreference.com/w/cpp/language/default_initialization
// i is POD and is not an array: therefore nothing is done.
//assert(i == ?);
// Value.
constexpr int j{};
// Following the cases at:
// https://en.cppreference.com/w/cpp/language/value_initialization
// j is not a class type, and is not an array type. Therefore it is zero initialized.
static_assert(j == 0);
// Direct.
constexpr int k{1};
// Following the cases at:
// https://en.cppreference.com/w/cpp/language/direct_initialization
// is not array, not class, not "if T is a non-class type but the source type is a
// class type" whatever that means, and not bool, therefore standard conversion is used
// and the value is set.
static_assert(k == 1);
}
}
// Now, let's see which implicit initializations are done for each case recursively.
{
{
struct C { int i; };
static_assert(std::is_default_constructible<C>());
static_assert(std::is_aggregate<C>());
// Default
C a;
// Following the cases at:
// https://en.cppreference.com/w/cpp/language/default_initialization
// - non-POD? no
// - array? no
// - then: nothing is done.
//assert(i == ?);
// Value
constexpr C b{};
// Following the cases at:
// https://en.cppreference.com/w/cpp/language/value_initialization
// - class type with no default constructor? no
// - default constructor that is neither user-provided nor deleted: yes
// - zero initialize
// - has a non-trivial default constructor
// - no
assert(b.i == 0);
// Aggregate
constexpr C c{5};
static_assert(c.i == 5);
// Zero
static C d;
assert(d.i == 0);
}
{
struct C {
int i;
constexpr C() : i(3) {};
constexpr C(int i) : i(i) {};
};
static_assert(!std::is_pod<C>());
// Default
constexpr C a;
// Following the cases at:
// https://en.cppreference.com/w/cpp/language/default_initialization
// - non-POD? yes. Therefore: call C(). which initializes i.
static_assert(a.i == 3);
// Value
constexpr C b{};
// Following the cases at:
// https://en.cppreference.com/w/cpp/language/value_initialization
// - class type with no default constructor? no
// - user-provided default constructor? yes. Therefore, default initialize the object.
// So we fall on the above case, and the variable does get set.
static_assert(b.i == 3);
// Direct
constexpr C c{5};
// Following the cases at:
// https://en.cppreference.com/w/cpp/language/_initialization
// - array type? no
// - class type? yes. Call constructor.
static_assert(c.i == 5);
}
{
struct C {
int i;
constexpr C(int i) : i(i) {};
};
static_assert(!std::is_pod<C>());
static_assert(!std::is_default_constructible<C>());
#if 0
// Cannot be default initialized if not default constructible.
// error: no matching function
constexpr C a;
#endif
#if 0
// Cannot be value initialized if not default constructible.
// error: no matching function
C b{};
#endif
// Direct initialize
constexpr C c{5};
static_assert(!std::is_pod<C>());
// - class type with no default constructor? yes
}
//struct A { T t; A() : t() {} };
//A a; // t is value-initialized
//A a{}; // t is value-initialized
//struct A { T t; A() : t{} {} };
//A a; // t is TODO
//A a{}; //
//struct A { T t; A() {} };
//A a; // t is TODO
//A a{}; // t is TODO
//struct A { T t; A() = default };
//A d; // t is TOAO
//A d{}; // t is TOAO
//struct A { T t; A() = deleted };
//A e; // t is TODO
//A e{}; // t is TODO
//struct A { T t; A(T t) {} };
//A f; // t is TODO
//A f{}; // t is TODO
}
}

View File

@@ -1,20 +1,18 @@
// https://cirosantilli.com/linux-kernel-module-cheat#cpp // https://cirosantilli.com/linux-kernel-module-cheat#cpp
#include <cassert>
int main() { int main() {
struct D { struct D {
int i; int i;
D() {} constexpr D() : i(0) {}
D(int i) : i(i) {} constexpr D(int i) : i(i) {}
}; };
struct C { struct C {
int i; int i;
C() : i(1) {} constexpr C() : i(1) {}
C(int i) : i(i) {} constexpr C(int i) : i(i) {}
C(const D& d) : i(d.i) {} constexpr C(const D& d) : i(d.i) {}
}; };
// Declares *FUNCTION* called `c` that returns `C` inside function main. // Declares *FUNCTION* called `c` that returns `C` inside function main.
@@ -25,7 +23,7 @@ int main() {
// Therefore there would be not way for C++ to distinguish between the two, // Therefore there would be not way for C++ to distinguish between the two,
// and still be backwards compatible with C. // and still be backwards compatible with C.
{ {
C c(); constexpr C c();
#if 0 #if 0
// ERROR: function definition is not possible inside another function. // ERROR: function definition is not possible inside another function.
@@ -37,24 +35,24 @@ int main() {
// If you want to call a default constructor, use: // If you want to call a default constructor, use:
{ {
C c; constexpr C c;
assert(c.i == 1); static_assert(c.i == 1);
} }
// For non-default constructors, literal arguments disambiguate // For non-default constructors, literal arguments disambiguate
// things as this syntax could not possibly be a function declaration. // things as this syntax could not possibly be a function declaration.
{ {
C c(2); constexpr C c(2);
assert(c.i == 2); static_assert(c.i == 2);
} }
// But sometimes even arguments are not enough: here D() // But sometimes even arguments are not enough: here D()
// is interpreted as "a function of type `D f()`" // is interpreted as "a function of type `D f()`"
{ {
C c(D(2)); constexpr C c(D());
#if 0 #if 0
// error: request for member i in c, which is of non-class type main()::C(main()::D (*)()) // error: request for member i in c, which is of non-class type main()::C(main()::D (*)())
assert(c.i == 2); static_assert(c.i == 0);
#endif #endif
} }
@@ -63,22 +61,22 @@ int main() {
{ {
// Extra parenthesis. // Extra parenthesis.
{ {
C c((D(2))); constexpr C c((D(2)));
assert(c.i == 2); static_assert(c.i == 2);
} }
// Initialize through assignment. TODO likely guaranteed to be cost-free, // Initialize through assignment. TODO likely guaranteed to be cost-free,
// but confirm. // but confirm.
{ {
C c = C((D(2))); constexpr C c = C((D(2)));
assert(c.i == 2); static_assert(c.i == 2);
} }
// Initializer list. Only works if there is no initializer_list constructor. // 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. // Only works in general if c does not have an ambiguous initializer_list constructor though.
{ {
C c{D(2)}; constexpr C c{D(2)};
assert(c.i == 2); static_assert(c.i == 2);
} }
} }
} }