Skip to content

Commit f58a74b

Browse files
committed
Align cpp demos with docs 31-40 (decay through explicit_constructor)
Each cpp mirrors only what its doc covers, with short labeled demos and didactic naming. - Create src/decay.cpp: array-to-pointer decay (sizeof inside fn vs main), preventing decay via T(&)[N], function-to-pointer decay, std::decay_t identity table. - src/class/default_0_delete_meaning.cpp: =default trivial-ctor check, Noncopyable via =delete, move-only mix, =0 pure virtual Shape, implicit default-ctor walkthrough (A/B/C/E). - src/double_dispatch.cpp: minimal Shape/Circle/Rectangle with two-level virtual dispatch via collideWith + collideWithCircle/Rectangle. Replaces the prior SpaceShip/Asteroid hierarchy that wasn't in the doc. - src/dynamic_memory_allocation.cpp: C-style malloc/calloc/realloc demos (doc covers C allocation, not new/delete). - src/enum.cpp: unscoped enum with bitmask via |, enum class, explicit underlying type, cross-enum comparison danger, iterating None..All. - src/error_handling.cpp + src/error_code.cpp: errno + strerror + perror, out-param error_code, system_category() wrapping errno, filesystem non-throwing overload, std::errc, custom parse_category with is_error_code_enum, system_error throwing form. - src/exception_handling.cpp: try/catch/catch(...), bad_alloc + nothrow, bad_cast/bad_typeid, logic/runtime_error subclasses, custom noexcept-override what(), stack-unwinding LIFO trace, noexcept fn. - Create src/execution_policies.cpp: std::execution::seq with for_each, transform, reduce, sort. par/par_unseq gated behind #if 0 with a note about needing libtbb linked. - src/class/explicit_constructor.cpp: implicit-conversion trap + explicit disables it, with a commented line showing what would not compile.
1 parent 92d4879 commit f58a74b

11 files changed

Lines changed: 734 additions & 371 deletions

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ add_executable(dynamic_memory_allocation src/dynamic_memory_allocation.cpp)
7373
add_executable(template src/template.cpp)
7474
add_executable(inline_functions src/inline_functions.cpp)
7575
add_executable(algorithms_library src/algorithms_library.cpp)
76+
add_executable(execution_policies src/execution_policies.cpp)
7677
add_executable(vector src/vector.cpp)
7778
add_executable(queue src/queue.cpp)
7879
add_executable(variadic_templates src/variadic_templates.cpp)
@@ -97,6 +98,7 @@ add_executable(literals src/literals.cpp)
9798
add_executable(ternary src/ternary.cpp)
9899
add_executable(lists src/lists.cpp)
99100
add_executable(type_traits src/type_traits.cpp)
101+
add_executable(decay src/decay.cpp)
100102
add_executable(typedef_type_alias_using_keyword src/typedef_type_alias_using_keyword.cpp)
101103
add_executable(most_vexing_parse src/most_vexing_parse.cpp)
102104
add_executable(VTABLE_and_VPTR src/VTABLE_and_VPTR.cpp)
Lines changed: 105 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,109 @@
1-
// Default constructors
2-
3-
class foo {
4-
public:
5-
/*
6-
=0
7-
C++ uses the special syntax = 0; to indicate pure virtual functions instead of
8-
adding a new keyword to the language
9-
*/
10-
void virtual pureVirtualFunction() = 0;
11-
/*
12-
=default
13-
It means that you want to use the compiler-generated version of that function,
14-
so you don't need to specify a body.
15-
*/
16-
~foo() = default;
17-
/*
18-
= delete
19-
It means that the compiler will not generate those constructors for you.
20-
This is only allowed on copy constructor and assignment operator
21-
*/
22-
foo(const foo &t) = delete;
23-
foo &operator=(const foo &t) = delete;
1+
// Meaning of =default, =delete, and =0 in C++.
2+
//
3+
// =default : ask the compiler to generate the special member function.
4+
// =delete : forbid the function; calling it is a compile error.
5+
// =0 : on a virtual function, declare it pure virtual (abstract).
6+
7+
#include <iostream>
8+
#include <type_traits>
9+
10+
// ---------- =default ----------
11+
// Foo has a user-defined constructor, so the compiler would NOT generate a
12+
// default constructor. We explicitly default it so Foo() still works.
13+
struct Foo {
14+
int i;
15+
Foo(int a, int b) : i(a), j(b) {}
16+
Foo() = default; // explicitly defaulted -> Foo stays trivial-ish
17+
private:
18+
int j;
19+
};
20+
21+
// Replacing `Foo() = default;` with `Foo() {}` would make it non-trivial.
22+
struct FooEmptyBody {
23+
int i;
24+
FooEmptyBody(int a, int b) : i(a), j(b) {}
25+
FooEmptyBody() {} // user-provided empty body -> non-trivial
26+
private:
27+
int j;
28+
};
29+
30+
// ---------- =delete ----------
31+
// Noncopyable: copy operations are forbidden.
32+
struct Noncopyable {
33+
Noncopyable() = default;
34+
Noncopyable(const Noncopyable&) = delete;
35+
Noncopyable& operator=(const Noncopyable&) = delete;
36+
};
37+
38+
// Movable-only: copies deleted, moves defaulted.
39+
struct MoveOnly {
40+
MoveOnly() = default;
41+
MoveOnly(const MoveOnly&) = delete;
42+
MoveOnly& operator=(const MoveOnly&) = delete;
43+
MoveOnly(MoveOnly&&) = default;
44+
MoveOnly& operator=(MoveOnly&&) = default;
45+
~MoveOnly() = default;
46+
};
47+
48+
// ---------- =0 (pure virtual) ----------
49+
struct Shape {
50+
virtual double area() const = 0; // pure virtual -> Shape is abstract
51+
virtual ~Shape() = default;
52+
};
53+
54+
struct Square : Shape {
55+
double side;
56+
Square(double s) : side(s) {}
57+
double area() const override { return side * side; }
58+
};
59+
60+
// ---------- Implicit deletion examples (from the doc) ----------
61+
struct A {
62+
int x;
63+
A(int x = 1) : x(x) {} // user-defined default constructor
64+
};
65+
66+
struct B : A { }; // B() implicitly defined, calls A::A()
67+
68+
struct C { A a; }; // C() implicitly defined, calls A::A()
69+
70+
struct E : A {
71+
E(int y) : A(y) {}
72+
E() = default; // explicitly defaulted, calls A::A()
2473
};
2574

2675
int main() {
27-
// foo
76+
std::cout << "--- =default makes Foo trivially-constructible ---\n";
77+
Foo f;
78+
std::cout << "Foo() compiles, Foo.i = " << f.i << "\n";
79+
std::cout << "is_trivially_default_constructible<Foo> = "
80+
<< std::is_trivially_default_constructible<Foo>::value << "\n";
81+
std::cout << "is_trivially_default_constructible<FooEmpty> = "
82+
<< std::is_trivially_default_constructible<FooEmptyBody>::value
83+
<< " (empty body {} is NOT trivial)\n";
84+
85+
std::cout << "\n--- =delete forbids copies ---\n";
86+
Noncopyable n1;
87+
// Noncopyable n2 = n1; // would not compile: copy ctor deleted
88+
// n1 = n1; // would not compile: copy assign deleted
89+
(void)n1;
90+
std::cout << "Noncopyable built; copy/assign would be a compile error\n";
91+
92+
std::cout << "\n--- move-only via =delete + =default ---\n";
93+
MoveOnly m1;
94+
MoveOnly m2 = std::move(m1); // move ok
95+
// MoveOnly m3 = m2; // would not compile: copy deleted
96+
(void)m2;
97+
std::cout << "MoveOnly moved; copy would be a compile error\n";
98+
99+
std::cout << "\n--- =0 pure virtual makes a class abstract ---\n";
100+
// Shape s; // would not compile: abstract type
101+
Square sq(3.0);
102+
Shape& sref = sq;
103+
std::cout << "Square(3).area() via Shape& = " << sref.area() << "\n";
104+
105+
std::cout << "\n--- implicit default ctor follows from members/bases ---\n";
106+
A a; B b; C c; E e;
107+
std::cout << "A.x=" << a.x << " B.x=" << b.x
108+
<< " C.a.x=" << c.a.x << " E.x=" << e.x << "\n";
28109
}

src/class/explicit_constructor.cpp

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,60 @@
1+
// Explicit Constructor
2+
//
3+
// The compiler is allowed to make ONE implicit conversion to resolve the
4+
// arguments of a function/constructor. A single-argument constructor that is
5+
// not marked `explicit` therefore doubles as an implicit conversion from its
6+
// parameter type. This is often surprising. The `explicit` keyword disables
7+
// that conversion: the constructor can still be called, but only directly.
8+
19
#include <iostream>
210

311
namespace Implicit {
412
class String {
513
public:
614
// allocate n bytes to the String object
715
String(int n) {
8-
std::cout << "allocate " << n << " bytes to the String object" << std::endl;
16+
std::cout << " allocate " << n << " bytes to the String object"
17+
<< std::endl;
918
}
1019

11-
// initializes object with char *p
20+
// initialize object with char *p
1221
String(const char *p) {
13-
std::cout << "initializes object with char *p" << std::endl;
22+
std::cout << " initialize object with char *p" << std::endl;
1423
}
1524
};
16-
1725
} // namespace Implicit
1826

1927
namespace Explicit {
20-
2128
class String {
2229
public:
2330
// allocate n bytes
2431
explicit String(int n) {
25-
std::cout << "allocate " << n << " bytes to the String object" << std::endl;
32+
std::cout << " allocate " << n << " bytes to the String object"
33+
<< std::endl;
2634
}
27-
// initialize sobject with string p
35+
// initialize object with char *p
2836
String(const char *p) {
29-
std::cout << "initializes object with char *p" << std::endl;
37+
std::cout << " initialize object with char *p" << std::endl;
3038
}
3139
};
32-
3340
} // namespace Explicit
3441

3542
int main() {
36-
// This is actually possible because Entity has a constructor that can get
37-
// only an integer or a string
43+
std::cout << "--- Implicit conversion trap ---" << std::endl;
44+
// 'a' is a char, implicitly convertible to int (ASCII 97).
45+
// Copy-initialization picks String(int) and allocates 97 bytes,
46+
// which is almost certainly not what the caller meant.
3847
Implicit::String s1 = 'a';
3948

40-
// explicit keyword will disable the above conversion, so we should do it by
41-
// casting
49+
std::cout << "--- explicit disables the trap ---" << std::endl;
50+
// With `explicit String(int)`, the following line no longer compiles:
51+
//
52+
// Explicit::String s2 = 'a'; // error: copy-init cannot use
53+
// // explicit constructor
54+
//
55+
// We must convert the char ourselves. Casting to const char* selects
56+
// the String(const char *) constructor instead.
4257
Explicit::String s2 = (const char *)'a';
58+
59+
return 0;
4360
}

src/decay.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Demo: decay in C++
2+
// Covers array-to-pointer decay, function-to-pointer decay,
3+
// preventing array decay via reference, and std::decay_t.
4+
5+
#include <iostream>
6+
#include <type_traits>
7+
8+
// Receives the array by value: it decays to int*.
9+
void takesPointer(int arr[10]) {
10+
std::cout << " inside takesPointer, sizeof(arr) = "
11+
<< sizeof(arr) << " (size of a pointer)\n";
12+
}
13+
14+
// Receives the array by reference: no decay, size preserved.
15+
template <std::size_t N>
16+
void takesArrayByRef(int (&arr)[N]) {
17+
std::cout << " inside takesArrayByRef, N = " << N
18+
<< ", sizeof(arr) = " << sizeof(arr) << "\n";
19+
}
20+
21+
// A plain function used to show function-to-pointer decay.
22+
int add(int a, int b) {
23+
return a + b;
24+
}
25+
26+
// Receives a function: name decays to a pointer.
27+
void callIt(int (*f)(int, int)) {
28+
std::cout << " callIt result = " << f(2, 3) << "\n";
29+
}
30+
31+
int main() {
32+
std::cout << "--- array-to-pointer decay ---\n";
33+
int arr[10];
34+
std::cout << " in main, sizeof(arr) = " << sizeof(arr)
35+
<< " (10 * sizeof(int))\n";
36+
takesPointer(arr);
37+
38+
std::cout << "--- preventing array decay (pass by reference) ---\n";
39+
takesArrayByRef(arr);
40+
41+
std::cout << "--- function-to-pointer decay ---\n";
42+
callIt(add); // 'add' decays to a function pointer
43+
44+
std::cout << "--- std::decay_t ---\n";
45+
std::cout << std::boolalpha;
46+
std::cout << " decay_t<int[10]> is int*: "
47+
<< std::is_same_v<std::decay_t<int[10]>, int*> << "\n";
48+
std::cout << " decay_t<int(int,int)> is int(*)(int,int): "
49+
<< std::is_same_v<std::decay_t<int(int, int)>, int(*)(int, int)> << "\n";
50+
std::cout << " decay_t<const int&> is int: "
51+
<< std::is_same_v<std::decay_t<const int&>, int> << "\n";
52+
53+
return 0;
54+
}

0 commit comments

Comments
 (0)