[toc]

lvalue && rvalue (C++11)

Understanding lvalues and rvalues in C and C++ - Eli Bendersky’s website (thegreenplace.net)

  • An lvalue (locator value) represents an object that occupies some identifiable location in memory (i.e. has an address).
  • rvalues are defined by exclusion, by saying that every expression is either an lvalue or an rvalue. Therefore, from the above definition of lvalue, an rvalue is an expression that does not represent an object occupying some identifiable location in memory.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<iostream>
#include<string>

void PrintName(const std::string& name)
{
std::cout << "[lvalue]" << name << std::endl;
}


void PrintName(const std::string&& name)
{
std::cout << "[rvalue]" << name << std::endl;
}

int main()
{
std::string firstname = "Randolf";
std::string secondname = "luo";
PrintName(firstname + secondname);
PrintName(firstname);
}
//[rvalue]Randolfluo
//[lvalue]Randolf

move semantic

vb c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include <iostream>
#include <string>

class String {
public:
String() = default; // Default constructor
// Constructor accepting string literal
String(const char* string) {
printf("Created!\n");
m_Size = strlen(string);
m_Data = new char[m_Size];
memcpy(m_Data, string, m_Size);
}
// Copy constructor to perform deep copy
String(const String& other) {
printf("Copied!\n");
m_Size = other.m_Size;
m_Data = new char[m_Size];
memcpy(m_Data, other.m_Data, m_Size);
}
// Move constructor to perform shallow copy
String(String&& other) noexcept {
printf("Moved!\n");
m_Size = other.m_Size;
m_Data = other.m_Data;

other.m_Size = 0;
other.m_Data = nullptr;
}
// Move assignment operator
String& operator=(String&& other) noexcept {
printf("Moved!\n");
if (this != &other) {
delete[] m_Data;
m_Size = other.m_Size;
m_Data = other.m_Data;

other.m_Size = 0;
other.m_Data = nullptr;
}
return *this;
}
// Destructor to release dynamically allocated memory
~String() {
delete[] m_Data;
printf("Destroyed!\n");
}
// Method to print the contents of the String object
void Print() {
for (uint32_t i = 0; i < m_Size; i++) {
printf("%c", m_Data[i]);
}
printf("\n");
}

private:
char* m_Data; // Pointer to the string data
uint32_t m_Size; // Size of the string
};

class Entity {
private:
String m_Name; // A member variable of type String
public:
// Constructor accepting a lvalue reference, used to initialize Entity object
Entity(const String& name) : m_Name(name) {
}
// Constructor accepting a rvalue reference, used to initialize Entity object
Entity(String&& name) : m_Name(std::move(name)) {
}
// Method to print the name of the Entity
void PrintName() {
m_Name.Print();
}
};

int main() {
// Creating an Entity object with a string literal (lvalue)
printf("--------------------------------------\n");
Entity e("Randolfluo");
e.PrintName();
printf("--------------------------------------\n");
// Creating a String object with a string literal
String arr = "Ran";
// Creating an Entity object with a named String object (lvalue)
Entity e1(arr);
e1.PrintName();

printf("--------------------------------------\n");
// Creating two String objects
String arr1 = "Apple";
String arr2;
arr1.Print();
arr2.Print();
printf("--------------------------------------\n");
// Moving the content of one String object to another
arr2 = std::move(arr1);
arr1.Print();
arr2.Print();
printf("--------------------------------------\n");

std::cin.get();
return 0;
}
/*
--------------------------------------
Created!
Moved!
Destroyed!
Randolfluo
--------------------------------------
Created!
Copied!
Ran
--------------------------------------
Created!
Apple

--------------------------------------
Moved!

Apple
--------------------------------------*/

//TODO

std::move

move assignment operator

perfect forward

Structured binding C++17

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <string>
#include <tuple>

std::tuple<std::string, int> CreatePersion()
{
return { "Randolfluo", 19 };
}

int main()
{
auto person = CreatePersion();
std::string& name = std::get<0>(person);
int age = std::get<1>(person);

std::string name;
int age;
std::tie(name, age) = CreatePersion();

auto [name, age] = CreatePersion();

}

std::optional C++17

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include<iostream>
#include<fstream>
#include<optional>

std::optional<std::string> ReadFileAsString(const std::string& filepath)
{
std::ifstream stream(filepath);
if (stream)
{
std::string result;
//read file
stream.close();
return result;
}
return {};
}

int main()
{
std::optional<std::string> data = ReadFileAsString("data.txt");
std::string value = data.value_or("Not present");
std::cout << value << std::endl;

if (data)
{
std::cout << "File read sucessfully!\n";
}
else
{
std::cout << "File could not be opened!\n";
}
std::cin.get();
}

std::variant

  • variant is more type-safe than union, but less efficient
  • variant is more type-safe than any
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include<iostream>
#include<fstream>
#include<variant>
#include<string>
enum class ErrorNum
{
None = 0, NotFound = 1, NoAccess = 2
};

std::variant<std::string,ErrorNum> ReadFileAsString(const std::string& filepath)
{
std::ifstream stream(filepath);

std::variant<std::string, ErrorNum> result ;

if (stream.is_open())
{
std::string line;
std::string content;
while (std::getline(stream, line))
{
content += line +'\n';
}
result = content;
}
else
{
return ErrorNum::None;
}
return result;
}

int main()
{
std::variant<std::string, ErrorNum> data;
data = ReadFileAsString("data.txt");
if (std::get_if<std::string>(&data))
{

std::cout << "File read sucessfully!\n";
std::cout << std::get<std::string>(data) << std::endl;
}
else
{
std::cout << "File could not be opened!\n";
}
std::cin.get();
}

std::any C++17

  • Small type, similar to struct.
  • Large types, dynamically allocated.

std::asnyc

//TODO

1

std::string

  • std::string_view C++17
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include<iostream>
#include<string>

static uint32_t s_AllocCount = 0;

void* operator new(size_t size)
{
s_AllocCount++;
std::cout << "Allocating " << size << "byte!\n";
return malloc(size);
}


void PrintName(const std::string& name)
{
std::cout << name << std::endl;
}

void PrintName1(std::string_view name)
{
std::cout << name << std::endl;
}

int main()
{
std::string name = "Randolf luo123412341234"; //More than 16 bytes are used to call malloc C++20 x64 msvc release
std::string firstName = name.substr(0, 16); //calling malloc
PrintName(firstName);
std::string lastName = name.substr(8, 10); //malloc was not called
PrintName(lastName);
std::cout << s_AllocCount << " allocations\n";
s_AllocCount = 0;
std::cout << "------------------------------------\n";

std::string name1 = "Randolf luo123412341234";
std::string_view firstName1(name1.c_str(), 17);
PrintName1(firstName1);
std::string_view lastName1(name1.c_str() + 8, 3);
PrintName1(lastName1);
std::cout << s_AllocCount << " allocations\n";
s_AllocCount = 0;
std::cout << "------------------------------------\n";

const char* name2 = "Randolf luo123412341234";
std::string_view firstName2(name2, 17);
PrintName1(firstName2);
std::string_view lastName2(name2 + 8, 3);
PrintName1(lastName2);
std::cout << s_AllocCount << " allocations\n";
s_AllocCount = 0;
std::cout << "------------------------------------\n";

}

string