Skip to content

Commit bf2571d

Browse files
committed
Initial commit
0 parents  commit bf2571d

File tree

597 files changed

+28953
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

597 files changed

+28953
-0
lines changed

`auto`-type-deduction.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# `auto`-type-deduction
2+
3+
The type deduction rules for auto are largely the same as for templates.
4+
In an expression like this:
5+
6+
const auto x = 5;
7+
8+
auto takes the role of T in template type deduction and the _type
9+
specifier_ (const auto) takes the role of the parameter type Type.
10+
11+
Thus all these cases will be the same as for template type deduction:
12+
13+
auto x = 5; // auto deduces to int
14+
15+
const auto& rx = x; // auto deduces to int, the specifier to const int&
16+
17+
auto&& urx1 = x; // auto deduces to int&, the specifier too
18+
19+
auto&& urx2 = 27; // auto decues to int, the specifier to int&&
20+
21+
auto* px = &x; // auto deduces to int, the specifier to int*
22+
23+
int arr [2] = {1, 2};
24+
25+
auto a = arr; // auto deduces to int* (array decays)
26+
27+
auto& ra = arr; // auto deduces to int (&) [2]
28+
29+
void f(int, std::string);
30+
31+
auto g = f; // auto deduces to void (*) (int, std::string)
32+
33+
auto& g = f; // auto deduces to void (&) (int, std::string)
34+
35+
There is __one__ important difference, regarding initializer-lists:
36+
template-type deduction for a brace-initialized sequence fails, while
37+
auto will deduce it as an std::initializer_list<T> after T has been
38+
deduced from the elements in the list (if possible, i.e. if all elements
39+
in the list have a single type, else fails too). Examples:
40+
41+
auto a = 5; // auto -> int
42+
auto b(5); // auto -> int
43+
auto c = {5}; // auto -> std::initializer_list<int>
44+
auto d{5}; // auto -> std::initializer_list<int> or int -- CAREFUL!
45+
auto e = {5, 1.0}; // fails (not only one type)
46+
47+
Special care must be taken for d. As of now, this does initialize an
48+
std::initializer_list. To ensure uniform initialization capabilities,
49+
however, this will _very soon_ change to initialize an integer and many
50+
compilers already implement it while others warn about it. So if you
51+
want to store an initializer list in an auto variable, use = {...} and
52+
not {...}.
53+
54+
Why does auto differ from template type deduction for initializer lists?
55+
Because template type deduction never implicitly converts anything.
56+
Consider this case:
57+
58+
template<typename T>
59+
void assign(T& target, const T& source);
60+
61+
std::vector<int> v;
62+
63+
assign(v, {1, 2, 3});
64+
65+
In this case, were {1, 2, 3} automatically deduces to be
66+
std::initializer_list<int>, this method call would fail because the
67+
types for T would differ. Without this automatic deduction (currently in
68+
place), T is deduces to be std::vector<int> and {1, 2, 3} initializes
69+
the source vector correctly.
70+
71+
The fact that auto automatically deduces a brace-list as an
72+
std::initializer_list is a polarizing topic and there exist proposals
73+
also to ban it.
74+
75+
A last note about auto in lambdas or return types: there, template type
76+
deduction rules apply and not auto type deduction rules:
77+
78+
template<typename Function>
79+
void f(Function function)
80+
{
81+
// Fails, template type deduction does not automatically
82+
// deduce this as std::initializer_list
83+
function({1, 2, 3});
84+
}
85+
86+
auto g()
87+
{
88+
// Also fails for same reasons as above.
89+
return {1, 2, 3};
90+
}
91+
92+
// Initializes a valid std::initializer_list
93+
auto list = {1, 2, 3};

`decltype`-deduction-rules.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# `decltype`-deduction-rules
2+
3+
The great thing about decltype is that it (almost) does not have any
4+
fancy rules regarding what types it deduces (returns). In 99% of all
5+
cases, the type you get from decltype(x) is the type with which you
6+
declared x:
7+
8+
// decltype(i) = const int
9+
const int i = 0;
10+
11+
// decltype(object) = const T&
12+
// decltype(f) = bool(const T&)
13+
tempalte<typename T>
14+
bool f(const T& object);
15+
16+
// decltype(obj) = Object
17+
Object obj;
18+
19+
f(obj); // decltype(f(obj)) = bool
20+
21+
template<typename T>
22+
class Foo
23+
{
24+
/* ... */
25+
};
26+
27+
// decltype(foo) = Foo<int>
28+
Foo<int> foo;
29+
30+
Known corner case: decltype(object.member)
31+
32+
Normally, accessing an object's member either using dot-notation
33+
(object.member) or using arrow-notation with pointers (object->member)
34+
yields a reference to the member, i.e. if the member is an int the type
35+
of object.member will be int&. However, this expectation does not
36+
reflect in the result of a call to decltype for this scenario:
37+
38+
Object object;
39+
40+
decltype(object.member); // yields type of member, not type&
41+
42+
decltype((&object)->member); // also type&
43+
44+
The solution introduces the second peculiarity for decltype(), that
45+
decltype(x) can differ from decltype((x)). More specifically, in all
46+
cases but the above, calling decltype with a name prodcuces the type of
47+
that name, while wrapping that name in parantheses gives you an
48+
_expression_. An expression is more complicated than a name and _almost
49+
always_ produces a reference to the type of the name:
50+
51+
int x = 5;
52+
53+
decltype(x); // int
54+
55+
decltype((x)); // int&
56+
57+
This solves the problem for above (member access):
58+
59+
Object object;
60+
61+
decltype(object.member); // type
62+
63+
decltype((object.member)) // type&

abort()-and-exit().md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# abort()-and-exit()
2+
3+
There are two standard ways to terminate a program: std::abort() and
4+
std::exit()
5+
6+
std::abort() stops the program at this point and does not call any
7+
destructors of any object, it literally halts the program at that point
8+
in time, with not a single line of code executed further. Moreover,
9+
std::abort() sends a SIGABRT signal.
10+
11+
std::exit(int) also does not call destructors on automatic objects /
12+
temporary objects, that are destructed when they go out of scope, but
13+
does destruct static and globally scoped data. Moreover, you can
14+
register a specific function / handler that is called when std::exit is
15+
called. This function is set via std::atexit:
16+
17+
// Allocate some memory
18+
int* p = new int(5);
19+
// Will be called upon std::exit() call
20+
void foo()
21+
{
22+
    std::cout << "foo called" << std::endl;
23+
   
24+
    delete p;
25+
}
26+
int main(int argc, char * argv [])
27+
{
28+
    // Register the exit handler
29+
    std::atexit(foo);
30+
   
31+
    // another code for std::exit(int) is EXIT_FAILURE
32+
    std::exit(EXIT_SUCCESS);
33+
34+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# abstract-classes-without-pure-virtual-methods
2+
3+
If you want a class without pure virtual functions to be solely an
4+
abstract class, make the destructor pure virtual:
5+
6+
class Base
7+
8+
{
9+
10+
public:
11+
12+
     void do(){ }
13+
14+
     virtual ~Base() = 0;
15+
16+
};
17+
18+
I.e.: you want a base class for inheritance/polymorphism but don't want
19+
the base class to be instantiateable. 
20+
21+
That way, giving it a pure virtual function makes it an abstract class.
22+
However, you must implement that destructor somewhere, as the compiler
23+
will call it when a derived class’ destructor is called. 
24+
25+

accessing-the-global-scope.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# accessing-the-global-scope
2+
3+
use ::member to access global members
4+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# algorithms-that-expect-sorted-ranges
2+
3+
BINARY_SEARCH (true false if element in sequence, using bs)
4+
5+
EQUAL_RANGE (returns two iterators denoting start and end of a range of
6+
equal elements)
7+
8+
UPPER_BOUND (returns first element not greater, i.e. equal or less)
9+
10+
LOWER_BOUND (return first element not less, i.e. equal or greater)
11+
12+
SET_UNION (copies to a destination all elements contained in one or both
13+
ranges, for two ranges)
14+
15+
SET_DIFFERENCE (copies to a destination all elements from one range
16+
which are not contained in another range)
17+
18+
SET_INTERSECTION (copies to a destination all elements contained in both
19+
ranges)
20+
21+
SET_SYMMETRIC_DIFFERENCE (copies to a destination all elements contained
22+
in one or the other range (XOR))
23+
24+
MERGE (one pass of the mergesort algorithm)
25+
26+
INPLACE_MERGE (one pass of the inplace-mergesort algorithm)
27+
28+
INCLUDES (whether or not all elements from one range are also included
29+
in another range)
30+
31+
UNIQUE (removes all duplicates of any value)

alignof.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# alignof
2+
3+
To get the alignment requirements of a type use the alignof keyword or
4+
std::alignment_of<Type>::value.
5+
6+
std::alignment_of<Base>::value == alignof(Base);
7+
8+

allocators.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# allocators
2+
3+
Allocators are special template classes used by the standard containers
4+
to allocate and deallocate memory. If you want to define your own,
5+
follow this advice:
6+
7+
- Make your allocator a template, with the template parameter T
8+
representing the type of objects for which you are
9+
allocating memory.
10+
- Provide the internal typedefs ‘pointer' and ‘reference’, but always
11+
have ‘pointer’ be T* and ‘reference’ be T&.
12+
- Never give your allocators 'per-object state’, i.e. do not let them
13+
have non-static data (all allocators of one type should be
14+
the same).
15+
- Provide a nested re-bind template struct to make it possible to use
16+
the same allocator but for a different type.
17+
18+
The last point refers to the fact that all node-based (associative)
19+
containers in the standard do not use the allocator you pass to it (e.g.
20+
Allocator<int> for list<int>) but actually need to use the same
21+
allocator type, but for its private ‘node’ types (that hold the ’next’
22+
and ‘previous’ pointers for a linked list). I.e.:
23+
24+
template<typename T>
25+
struct Custom_Allocator
26+
{
27+
typedef T value_type;
28+
typedef T* pointer;
29+
typedef T& reference;
30+
T* allocate(std::size_t numberOfObjects);
31+
void deallocate(T* ptr, std::size_t numberOfObjects);
32+
// There is no such thing as a templated
33+
// typedef or using declaration, so you
34+
// provide this nested struct that is
35+
// itself templated and that just provides
36+
// the typedef to have access to the allocator
37+
// class but for a different type. If there
38+
// were no rebind struct there would be no way
39+
// to access the un-templated type of an allocator
40+
// passed to a container or so, since an instance
41+
// of a template class for a certain type is entirely
42+
// unaware that it is an instance of a template class
43+
// (since it is simply an instance of the statically,
44+
// compiler-generated version of the class for type T)
45+
template<typename U>
46+
struct rebind
47+
{
48+
typedef Custom_Allocator<U> other;
49+
};
50+
};
51+
template<typename Alloc>
52+
void foo(const Alloc& allocator)
53+
{
54+
// Alloc has no idea that it is actually of
55+
// type Custom_Allocator<T>
56+
typename Alloc::template rebind<std::string>::other
57+
allocator_for_string;
58+
// bla
59+
}
60+
int main(int argc, char * argv[])
61+
{
62+
Custom_Allocator<int> allocator_for_int;
63+
foo(allocator_for_int);
64+
65+
}
66+
67+
Note also the differences in signature between ‘operator new’ and
68+
the ‘allocate’ method of the Allocator:
69+
70+
void* operator new(std::size_t numberOfBytes);
71+
72+
T* Allocator<T>::allocate(std::size_t numberOfObjects);
73+
74+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# always-have-comparison-functions-return-false-for-equal-values
2+
3+
Associative containers work with equivalence, i.e. you pass them one
4+
comparison function for a < b and it then determines whether two values
5+
are _equivalent_ (similar to equal) by testing if !(a < b) && !(b < a).
6+
It does so more precisely by using the function you pass as a predicate,
7+
i.e. ! pred(a, b) && ! pred(b, a). This predicate MUST RETURN FALSE FOR
8+
EQUAL VALUES. Else you corrupt the container. The reason why is that
9+
when you insert a value into an associative container, it needs to check
10+
if the value already exists:
11+
12+
std::set<int> set{1};
13+
set.insert(1);
14+
15+
print(set.size()); // 1
16+
17+
If you now were to pass a predicate that returns true for equal values,
18+
the set would go ahead and test if:
19+
20+
!(a<=b) && !(b<=a)
21+
22+
if now a and be are equal, this yields:
23+
24+
!true && !true
25+
26+
which is
27+
28+
false && false
29+
30+
which ultimately evaluates to false. In explanation, this means that if
31+
you try to insert a value into a set that already exists in that set,
32+
the set would determine that the value is not yet present (because the
33+
equivalence check failed for all values) and thus insert it again,
34+
making the set not a set but something ugly.
35+
36+
int main(int argc, char * argv[])
37+
{
38+
auto predicate = [&] (int a, int b) { return a <= b; };
39+
std::set<int, decltype(predicate)> set({1}, predicate);
40+
set.insert(1);
41+
print(set.size()); // 2
42+
43+
}
44+
45+
Therefore don’t ever do it!

0 commit comments

Comments
 (0)