Skip to content

Commit

Permalink
Week 7
Browse files Browse the repository at this point in the history
  • Loading branch information
furkankirac committed Mar 21, 2024
1 parent a29f621 commit e6efcd5
Show file tree
Hide file tree
Showing 4 changed files with 283 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ add_executable(w5a1 "week5-app1.cpp")
add_executable(w5a2 "week5-app2.cpp")
add_executable(w6a1 "week6-app1.cpp")
add_executable(w6a2 "week6-app2.cpp")
add_executable(w7a1 "week7-app1.cpp")
add_executable(w7a2 "week7-app2.cpp")
add_executable(w7a3 "week7-app3.cpp")
48 changes: 48 additions & 0 deletions week7-app1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// parameter type binding exercises

#include <iostream>

template<typename> struct TypeDisplayer;

struct Widget { };

// (1) function with l-value ref
void f(Widget&) { std::cout << "1\n"; }

// (2) function with l-value ref-to-const
void f(const Widget&) { std::cout << "2\n"; }

// (3) function with r-value ref
void f(Widget&&) { std::cout << "3\n"; }

// (4) function with r-value ref-to-const
void f(const Widget&&) { std::cout << "4\n"; }

// (5) function template with forwarding ref
template<typename T>
void f(T&&) { std::cout << "5\n"; }

// (6) function template with r-value ref-to-const
template<typename T>
void f(const T&&) { std::cout << "6\n"; }

Widget getWidget() { Widget w; return w; }

const Widget getConstWidget() { const Widget w; return w; }

int main(int argc, char* argv[])
{
// what is the preference order of above functions for below questions?
Widget w1{}; // auto w1 = Widget{};
f(w1);

const Widget w2{};
f(w2);

f(getWidget());

f(getConstWidget());

return 0;
}

132 changes: 132 additions & 0 deletions week7-app2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#include <iostream>
//#include <string>

// move semantics pitfalls

// pitfall #1:
// T&& in deduced context is a forwarding reference
// it can match to both lvalues and rvalues.
// using std::move is an unconditional cast to rvalue.
// this is wrong if constructor of A is used with an lvalue in call site.
namespace PF1
{
struct B { };
struct A
{
B b_;

template<typename T>
A(T&& t) : b_{std::move(t)}
{ }
};
}

// pitfall #2:
// T&& here is not a forwarding reference!
// because T's deduction is done when struct A is instantiated.
// A's constructor is not itself templated. Hence, T&& means rvalue reference
// in an rvalue context, using std::forward is wrong
// std::forward is a CONDITIONAL cast to rvalue. However, we are sure that
// the context is already rvalue. std::move would be correct.
namespace PF2
{
struct B { };
template<typename T>
struct A
{
B b_;

A(T&& t) : b_{std::forward<T>(t)}
{ }
};
}

// pitfall #3:
// T&& is a forwarding reference.
// however, if A's contructor is called with an rvalue
// std::forward will cast t to &&.
// unfortunately there is only one 't' instance.
// first move constructor using 't' will steal its internals.
// 't' will be useless for its second usage.
// will cause major bug that cannot be easily detected
namespace PF3
{
struct B { };
struct C { };
struct A
{
B b_;
C c_;

template<typename T>
A(T&& t)
: b_{std::forward<T>(t)}
, c_{std::forward<T>(t)}
{
}
};
}


// pitfall #4:
// THIS WAS A TRICK DURING CLASS LECTURE. NOTHING IS WRONG WITH BELOW CODE
// looks like if t1 and t2 are both referencing the same value at the call-site, it is an error.
// but it is not. Because the dangerous thing is stealing/moving from the same value.
// However, two real r-values (temporaries) cannot be the same object at the call site.
// Imagine first r-value is 5, 2nd r-value is 5. There are two different 5s. It's not the same 5.
// So we can move them on their own. No stealing/moving happens on the same l-value.
namespace PF4
{
struct B { };
struct C { };
struct A
{
B b_;
C c_;

template<typename T1, typename T2>
A(T1&& t1, T2&& t2)
: b_{std::forward<T1>(t1)}
, c_{std::forward<T2>(t2)}
{
}
};
}

template<typename ...>
struct TypeDisplayer;

#include <type_traits>

// pitfall #5:
// std::is_integral<T>::value waits for a T that does not contain & or *
// However a forwarding reference may deduce T with a & inside.
// For instance, T can be deduced as float&
// passing float& to is_integral such as std::is_integral<float&> is wrong
// a reference is a pointer in its implementation
// pointers are memory addresses that are actually integral values
// in below code, you should remove a potential reference yourself and use that
// for instance use new type K in below code
template<typename T>
void foo(T&&)
{
// using K = std::remove_reference_t<T>;

// TypeDisplayer<K> a;
if constexpr(std::is_integral<T>::value) // use K here, instead of T
{
// deal with integral types: char, short, int, long, ...
}
else
{
// deal with non-integral types
}
}

int main(int argc, char* argv[])
{
int a = 5;
foo(a);

return 0;
}
100 changes: 100 additions & 0 deletions week7-app3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#include <iostream>
#include <vector>
#include <string>
#include <cstring>

using namespace std;

struct BigObject {
string s;
vector<char> data;

// BigObject(const char* s) {

// }

BigObject() = default;

template<typename T>
BigObject(T&& s, const char* p)
: s(std::forward<T>(s))
, data(strlen(p) + 1)
{
for(int i=0; i<data.size(); i++)
data[i] = p[i];
// if constexpr(std::is_same_v<T, string>) {

// }
}

template<typename T>
BigObject(T&& other)
: s(std::forward<T>(other.s))
, data(std::forward<T>(other.data))
{
// ...
// ...
// ...
// ...
// ...
// ...
// ...
// ...
}

// BigObject(const BigObject& other) : s(other.s), data(other.data)
// {
// // ...
// // ...
// // ...
// // ...
// // ...
// // ...
// }
// BigObject(BigObject&& other) : s(std::move(other.s)), data(std::move(other.data)) {
// // ...
// // ...
// // ...
// // ...
// // ...
// // ...
// // ...
// }

// BigObject(const string& s) : s(s) { }
// BigObject(string&& s) : s(std::move(s)) { }
};

auto foo(auto&& func) {
char data[] = {'h', 'i', ' ', 't', 'h', 'e', 'r', 'e', '!', 0};
auto bo = BigObject{"abc", data};
func();
return bo;
}


// struct FFFF {
// BigObject k;
// // void* _vtable; // 8 bytes

// void operator() () {
// cout << "I am a dying lambda" << endl;
// }

// // virtual void another() { }
// // virtual void another2() { }
// };

int main(int argc, char* argv[])
{
// auto f = FFFF{};
// auto g = FFFF{};
// cout << sizeof(f) << endl;
BigObject k;
auto f = []() { cout << "I am a dying lambda" << endl; };
BigObject bo = foo(f); // copy ctor? move ctor?

BigObject bo2 = std::move(bo);

return 0;
}

0 comments on commit e6efcd5

Please sign in to comment.