3-Types.ipynb 37.4 KB
Newer Older
1
2
3
4
5
6
{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
7
    "# [Getting started in C++](./) - [Procedural programming](./0-main.ipynb) - [Predefined types](./3-Types.ipynb)"
8
9
   ]
  },
10
11
12
13
14
15
16
17
18
19
  {
   "cell_type": "markdown",
   "metadata": {
    "toc": true
   },
   "source": [
    "<h1>Table of contents<span class=\"tocSkip\"></span></h1>\n",
    "<div class=\"toc\"><ul class=\"toc-item\"><li><span><a href=\"#Boolean\" data-toc-modified-id=\"Boolean-1\">Boolean</a></span></li><li><span><a href=\"#Enumerations\" data-toc-modified-id=\"Enumerations-2\">Enumerations</a></span><ul class=\"toc-item\"><li><span><a href=\"#Historical-enumerations\" data-toc-modified-id=\"Historical-enumerations-2.1\">Historical enumerations</a></span></li><li><span><a href=\"#New-enumerations\" data-toc-modified-id=\"New-enumerations-2.2\">New enumerations</a></span></li></ul></li><li><span><a href=\"#Numerical-types\" data-toc-modified-id=\"Numerical-types-3\">Numerical types</a></span><ul class=\"toc-item\"><li><ul class=\"toc-item\"><li><span><a href=\"#List-of-numerical-types\" data-toc-modified-id=\"List-of-numerical-types-3.0.1\">List of numerical types</a></span></li><li><span><a href=\"#Numeric-limits\" data-toc-modified-id=\"Numeric-limits-3.0.2\">Numeric limits</a></span></li><li><span><a href=\"#Conversions-between-digital-types\" data-toc-modified-id=\"Conversions-between-digital-types-3.0.3\">Conversions between digital types</a></span></li></ul></li><li><span><a href=\"#Explicit-conversions-inherited-from-C\" data-toc-modified-id=\"Explicit-conversions-inherited-from-C-3.1\">Explicit conversions inherited from C</a></span></li><li><span><a href=\"#Explicit-conversions-by-static_cast\" data-toc-modified-id=\"Explicit-conversions-by-static_cast-3.2\">Explicit conversions by static_cast</a></span></li><li><span><a href=\"#Other-explicit-conversions\" data-toc-modified-id=\"Other-explicit-conversions-3.3\">Other explicit conversions</a></span></li></ul></li><li><span><a href=\"#Characters-and-strings\" data-toc-modified-id=\"Characters-and-strings-4\">Characters and strings</a></span><ul class=\"toc-item\"><li><span><a href=\"#Historical-strings\" data-toc-modified-id=\"Historical-strings-4.1\">Historical strings</a></span></li><li><span><a href=\"#std::string\" data-toc-modified-id=\"std::string-4.2\">std::string</a></span></li></ul></li><li><span><a href=\"#Renaming-types\" data-toc-modified-id=\"Renaming-types-5\">Renaming types</a></span></li><li><span><a href=\"#decltype-and-auto\" data-toc-modified-id=\"decltype-and-auto-6\"><code>decltype</code> and <code>auto</code></a></span></li></ul></div>"
   ]
  },
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Boolean\n",
    "\n",
    "Variables with type `bool` may be set to true or false.\n",
    "\n",
    "It should be noted that this type did not originally exist, and that C++ instructions with conditions do not necessarily expect boolean values, but rather integers.\n",
    "\n",
    "There is a form of equivalence between booleans and integers: any null integer is equivalent to `false`, and any other value is equivalent to `true`."
   ]
  },
  {
   "cell_type": "code",
35
   "execution_count": null,
36
   "metadata": {},
37
   "outputs": [],
38
39
40
   "source": [
    "#include <iostream>\n",
    "\n",
41
42
43
    "bool undefined;  // UNDEFINED !! \n",
    "if (undefined)\n",
    "    std::cout << \"This text might appear or not - it's truly undefined and may vary from \"\n",
ROUVREAU Vincent's avatar
Typo    
ROUVREAU Vincent committed
44
    "                \"one run/compiler/architecture/etc... to another!\" << std::endl;"
45
46
47
48
49
50
51
52
53
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bool defined { true };\n",
54
    "\n",
55
56
57
58
59
60
61
62
63
64
65
    "if (defined)\n",
    "    std::cout << \"Defined!\" << std::endl;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "int n = -5;\n",
66
    "\n",
67
68
69
70
71
72
73
74
75
76
77
    "if (n) \n",
    "    std::cout << \"Boolean value of \" << n << \" is true.\" << std::endl;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "int n = 0;\n",
78
    "\n",
79
80
    "if (!n) // ! is the not operator: the condition is true if n is false.\n",
    "    std::cout << \"Boolean value of \" << n << \" is false.\" << std::endl;"
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Enumerations\n",
    "\n",
    "### Historical enumerations\n",
    "\n",
    "The historical enumerations `enum` of C++ allow to define constants that are treated as integers, and that can be initialized from integers. By default the first value is 0 and the `enum` is incremented for each value, but it is possible to bypass these default values and provide the desired numerical value yourself."
   ]
  },
  {
   "cell_type": "code",
96
   "execution_count": null,
97
   "metadata": {},
98
   "outputs": [],
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
   "source": [
    "#include <iostream>\n",
    "\n",
    "{\n",
    "    enum color { red, green, blue } ;\n",
    "    std::cout << red << \" \" << green << \" \" << blue << \" (expected: 0, 1, 2)\" << std::endl;\n",
    "\n",
    "    enum shape { circle=10, square, triangle=20 };\n",
    "    std::cout << circle << \" \" << square << \" \" << triangle << \" (expected: 10, 11, 20)\"<< std::endl;  //  10 11 20    \n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "These `enum` are placeholders for integers and might be used as such:"
   ]
  },
  {
   "cell_type": "code",
120
   "execution_count": null,
121
   "metadata": {},
122
   "outputs": [],
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
   "source": [
    "#include <iostream>\n",
    "\n",
    "{\n",
    "    enum color { red, green, blue } ;\n",
    "    int a { 5 };\n",
    "    color c = green;\n",
    "    int b = a + c;\n",
    "    std::cout << \"b = \" << b << \" (expected: 6)\" << std::endl;\n",
    "    \n",
    "    enum shape { circle=10, square, triangle=20 };\n",
    "    shape s = triangle;\n",
    "    int d = s + c;\n",
    "    std::cout << \"d = \" << d << \" (expected: 21... but we've just added a shape to a color without ado!)\" << std::endl;\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A shortcoming of historical `enum ` is that the same word can't be used in two different `enum`:"
   ]
  },
  {
   "cell_type": "code",
149
   "execution_count": null,
150
   "metadata": {},
151
   "outputs": [],
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
   "source": [
    "{\n",
    "    enum is_positive { yes, no };\n",
    "    \n",
    "    enum is_colored { yes, no }; // COMPILATION ERROR!\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### New enumerations\n",
    "\n",
    "To overcome the two limitations we have just mentioned, C++11 makes it possible to declare new `enum class` enumerations, each constituting a separate type, not implicitly convertible into an integer. This type protects against previous errors at the cost of a little more writing work."
   ]
  },
  {
   "cell_type": "code",
171
   "execution_count": null,
172
173
174
175
   "metadata": {},
   "outputs": [],
   "source": [
    "enum class is_positive { yes, no };    \n",
176
    "enum class is_colored { yes, no };  // OK    "
177
178
179
180
   ]
  },
  {
   "cell_type": "code",
181
   "execution_count": null,
182
   "metadata": {},
183
   "outputs": [],
184
185
186
187
188
189
   "source": [
    "yes; // COMPILATION ERROR: `enum class ` value must be prefixed! (see below)"
   ]
  },
  {
   "cell_type": "code",
190
   "execution_count": null,
191
192
193
194
195
196
197
198
   "metadata": {},
   "outputs": [],
   "source": [
    "is_positive p = is_positive::yes; // OK"
   ]
  },
  {
   "cell_type": "code",
199
   "execution_count": null,
200
   "metadata": {},
201
   "outputs": [],
202
   "source": [
203
    "int a = is_positive::no; // COMPILATION ERROR: not implicitly convertible into an integer"
204
205
206
207
   ]
  },
  {
   "cell_type": "code",
208
   "execution_count": null,
209
   "metadata": {},
210
   "outputs": [],
211
212
213
214
215
216
   "source": [
    "is_positive::yes + is_colored::no; // COMPILATION ERROR: addition of two unrelated types"
   ]
  },
  {
   "cell_type": "code",
217
   "execution_count": null,
218
219
220
221
222
223
   "metadata": {},
   "outputs": [],
   "source": [
    "{\n",
    "    enum class color { red, green, blue } ;\n",
    "    color c = color::green;\n",
224
    "    bool is_more_than_red = (c > color::red); // Both belong to the same type and therefore might be compared\n",
225
226
227
    "}"
   ]
  },
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "These enum types are really handy to make code more expressive, especially in function calls:\n",
    "\n",
    "````\n",
    "f(print::yes, perform_checks::no);\n",
    "````\n",
    "\n",
    "is much more expressive (and less error-prone) than:\n",
    "\n",
    "````\n",
    "f(true, false);\n",
    "````\n",
    "\n",
244
245
246
    "for which you will probably need to go check the prototype to figure out what each argument stands for.\n",
    "\n",
    "As we shall see [shortly](#Explicit-conversions-by-static_cast), you may perform arithmetic with the underlying integer through _explicit cast_ of the enum into an integer."
247
248
   ]
  },
249
250
251
252
253
254
255
256
257
258
259
260
261
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Numerical types\n",
    "\n",
    "#### List of numerical types\n",
    "\n",
    "The FORTRAN correspondences below are given as examples. The\n",
    "size of the C++ digital types can vary depending on the processor used. The\n",
    "standard C++ only imposes `short <= int <= long` and `float <= double <= long double`. This makes these predefined types unportable. Like many things\n",
    "in C, and therefore in C++, performance is given priority over any other consideration.\n",
    "The default integer and real types, `int` and `double`, are assumed\n",
262
    "to match the size of the processor registers and be the fastest (for more details see [the article on cppreference](http://en.cppreference.com/w/cpp/language/types))\n",
263
    "\n",
264
265
266
267
268
269
270
271
272
    "| C++           | Fortran   | Observations        | 0 notation |\n",
    "|:------------- |:---------:|:-------------------:|:----------:|\n",
    "| `short`       | INTEGER*2 | At least on 16 bits | None       |\n",
    "| `int`         | INTEGER*4 | At least on 16 bits | 0          | \n",
    "| `long`        | INTEGER*8 | At least on 32 bits | 0l         |\n",
    "| `long long`   | INTEGER*16| At least on 64 bits | 0ll        |\n",
    "| `float`       | REAL*4    | -                   | 0.f        |\n",
    "| `double`      | REAL*8    | -                   | 0.         |\n",
    "| `long double` | REAL*16   | -                   | 0.l        |\n",
273
274
275
276
277
278
279
280
    "\n",
    "All integer types (`short`, `int` and `long`) also have an unsigned variant, for example\n",
    "`unsigned int`, which only takes positive values.\n",
    "\n",
    "It should also be noted that the type `char` is the equivalent of one byte,\n",
    "and depending on the context will be interpreted as a number or as a\n",
    "character.\n",
    "\n",
281
282
    "If you need an integer type of a defined size, regardless of the type of processor or platform used, you should use those already defined in `<cstdint>` for C++11 (for more details click [here](http://en.cppreference.com/w/cpp/types/integer)).\n",
    "\n",
283
284
    "The _0 notation column_ is the way to notice explicitly the type in an expression; of course any value might be used instead of 0. A `u` might be used to signal the unsigned status for integer types; for instance `3ul` means 3 as an _unsigned long_. `auto` notation below will illustrate a case in which such a notation is useful.\n",
    "\n",
285
    "The STL features rather heavily a type named `std::size_t`, which by design is able to store the maximum size of a theoretically possible object of any type (including array). On most (all?) systems `std::size_t` is an alias to an `unsigned long`. More may be found about this type on [CppReference](https://en.cppreference.com/w/cpp/types/size_t). The equivalent counter part for signed integers is the [`std::ptrdiff_t`](https://en.cppreference.com/w/cpp/types/ptrdiff_t), which is the signed integer type of the result of subtracting two pointers. \n",
286
287
    "\n",
    "\n",
288
289
290
    "#### Numeric limits\n",
    "\n",
    "Always keep in mind the types of the computer don't match the abstract concept you may use in mathematics... The types stored especially don't go from minus infinity to infinity:"
291
292
293
294
   ]
  },
  {
   "cell_type": "code",
295
   "execution_count": null,
296
   "metadata": {},
297
   "outputs": [],
298
299
300
301
302
303
   "source": [
    "#include <iostream>\n",
    "#include <limits>  // for std::numeric_limits\n",
    "\n",
    "\n",
    "{\n",
304
    "    std::cout << \"int [min, max] = [\" << std::numeric_limits<int>::lowest() << \", \"\n",
305
306
    "            << std::numeric_limits<int>::max() << \"]\" << std::endl;\n",
    "\n",
307
    "    std::cout << \"unsigned int [min, max] = [\" << std::numeric_limits<unsigned int>::lowest() << \", \"\n",
308
309
    "            << std::numeric_limits<unsigned int>::max() << \"]\" << std::endl;    \n",
    "    \n",
310
    "    std::cout << \"short [min, max] = [\" << std::numeric_limits<short>::lowest() << \", \"\n",
311
312
    "            << std::numeric_limits<short>::max() << \"]\" << std::endl;\n",
    "    \n",
313
314
315
316
    "    std::cout << \"long [min, max] = [\" << std::numeric_limits<long>::lowest() << \", \"\n",
    "            << std::numeric_limits<long>::max() << \"]\" << std::endl;\n",
    "    \n",
    "    std::cout << \"float [min, max] = [\" << std::numeric_limits<float>::lowest() << \", \"\n",
317
318
    "            << std::numeric_limits<float>::max() << \"]\" << std::endl;\n",
    "\n",
319
    "    std::cout << \"double [min, max] = [\" << std::numeric_limits<double>::lowest() << \", \"\n",
320
321
    "            << std::numeric_limits<double>::max() << \"]\" << std::endl;\n",
    "\n",
322
    "    std::cout << \"long double [min, max] = [\" << std::numeric_limits<long double>::lowest() << \", \"\n",
323
    "        << std::numeric_limits<long double>::max() << \"]\" << std::endl;\n",
324
325
326
327
328
329
330
331
332
333
334
335
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If an initial value is not in the range, the compiler will yell:"
   ]
  },
  {
   "cell_type": "code",
336
   "execution_count": null,
337
   "metadata": {},
338
   "outputs": [],
339
340
   "source": [
    "#include <iostream>\n",
341
    "\n",
342
    "{\n",
343
    "    short s = -33010; // triggers a warning: outside the range\n",
344
345
346
347
348
349
350
351
352
353
354
355
356
    "    std::cout << s << std::endl;\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "However, if you go beyond the numeric limit during a computation you're on your own:"
   ]
  },
  {
   "cell_type": "code",
357
   "execution_count": null,
358
   "metadata": {},
359
   "outputs": [],
360
361
362
363
364
   "source": [
    "#include <iostream>\n",
    "#include <limits>  // for std::numeric_limits\n",
    "\n",
    "{\n",
365
    "    unsigned int max = std::numeric_limits<unsigned int>::max();\n",
366
367
368
    "    \n",
    "    std::cout << \"Max = \" << max << std::endl;\n",
    "    std::cout << \"Max + 1 = \" << max + 1 << \"!\" << std::endl;\n",
369
370
371
    "}"
   ]
  },
372
373
374
375
376
377
378
379
380
381
382
383
384
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When you reach the end of a type, a modulo is actually applied to make put it back into the range! \n",
    "\n",
    "Don't worry, for most computations you shouldn't run into this kind of trouble, but if you are dealing with important values it is important to keep in mind this kind of issues.\n",
    "\n",
    "The most obvious way to avoid this is to choose appropriate types: if your integer might be huge a `long` is more appropriate than an `int`.\n",
    "\n",
    "Other languages such as Python gets a underlying integer model that is resilient to this kind of issue but there is a cost behind; types such as those used in C++ are tailored to favor optimization on your hardware."
   ]
  },
385
386
387
388
389
390
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Conversions between digital types\n",
    "\n",
391
392
393
    "[Earlier](/notebooks/1-ProceduralProgramming/1-Variables.ipynb#Initialisation) I indicated there were small differences between the three initialization methods, that could be ignored most of the time. \n",
    "\n",
    "The difference is related to implicit conversion: both historical initialization methods are ok with implicit conversion __with accuracy loss__:"
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "{\n",
    "    float f = 1.12345678901234567890;\n",
    "    double d = 2.12345678901234567890;\n",
    "    float f_d(d);\n",
    "    float f_dd = d;\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
414
    "whereas C++ 11 introduced initialization with braces isn't:"
415
416
417
418
419
420
421
422
423
424
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "{\n",
    "    double d = 2.12345678901234567890;\n",
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
    "    float f_d{d}; // COMPILATION ERROR\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is really related to **accuracy loss**: initialization with braces is ok if there are none:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "{\n",
    "    float f = 1.12345678901234567890;\n",
    "    double d_f { f }; // OK\n",
445
446
447
448
449
450
451
452
453
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Accuracy losses are detected during conversion:\n",
    "* from a floating point type (`long double`, `double` and `float`) into an integer type.\n",
454
455
456
457
    "* from a `long double` into a `double` or a `float`, unless the source is constant and its value fits into the type of the destination.\n",
    "* from a `double` into a `float`, unless the source is constant and its value fits in the type of the destination.\n",
    "* from an integer type to an enumerated or floating point type, unless the source is constant and its value fits into the type of the destination.\n",
    "* from an integer type to an enumerated type or another integer type, unless the source is constant and its value fits into the type of the destination."
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Explicit conversions inherited from C\n",
    "\n",
    "In the case of an explicit conversion, the programmer explicitly says which conversion to use.\n",
    "C++ inherits the forcing mechanism of the C type:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "{\n",
    "    unsigned short i = 42000 ;\n",
    "    short j = short(i) ;\n",
    "    unsigned short k = (unsigned short)(j) ;\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
487
    "It is **not recommended** to use this type of conversion: even if it is clearly faster to type, it is less accurate and does not stand out clearly when reading a code; it is preferable to use the other conversion modes mentioned below."
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Explicit conversions by static_cast\n",
    "\n",
    "C++ has also redefined a family of type forcing,\n",
    "more verbose but more precise. The most common type of explicit conversion is the `static_cast`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "{\n",
    "    unsigned short i = 42000;\n",
    "    short j = static_cast<short>(i);\n",
    "    unsigned short k = static_cast<unsigned short>(j);\n",
    "}"
   ]
  },
513
514
515
516
517
518
519
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Another advantage of this more verbosy syntax is that you may find it more easily in your code with your editor search functionality."
   ]
  },
520
521
522
523
524
525
526
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Other explicit conversions\n",
    "\n",
    "There are 3 other types of C++ conversions:\n",
527
528
529
    "* `const_cast`, to add or remove constness to a reference or a pointer (obviously to be used with great caution!)\n",
    "* `dynamic_cast`, which will be introduced when we'll deal with polymorphism.\n",
    "* `reinterpret_cast`, which is a very brutal cast which changes the type into any other type, regardless of the compatibility of the two types considered. It is a dangerous one that should be considered only in very last resort (usually when interacting with a C library)."
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Characters and strings\n",
    "\n",
    "### Historical strings\n",
    "\n",
    "In C, a character string is literally an array of `char` variables, the last character of which is by convention the symbol `\\0`.\n",
    "\n",
    "The `strlen` function returns the length of a string, which is the number of characters between the very first character and the first occurrence of `\\0`.\n",
    "\n",
    "The `strcpy` function copies a character string to a new memory location; care must be taken to ensure that the destination is large enough to avoid any undefined behavior.\n",
    "\n",
546
    "The `strncpy` function allows you to copy only the first <b>n</b> first characters, where <b>n</b> is the third parameter of the function. Same remark about the need to foresee a large enough destination.\n"
547
548
549
550
   ]
  },
  {
   "cell_type": "code",
551
   "execution_count": null,
552
   "metadata": {},
553
   "outputs": [],
554
555
556
557
   "source": [
    "#include <iostream>\n",
    "#include <cstring>  // For strlen, strcpy, strncpy\n",
    "\n",
558
    "char hello[] = {'h','e','l','l','o', '\\0'};\n",
559
    "\n",
560
    "char copy[6] = {}; // = {'\\0','\\0','\\0','\\0','\\0','\\0' };\n",
561
    "\n",
562
563
564
565
566
567
568
569
570
571
572
573
574
575
    "strcpy(copy, hello);\n",
    "std::cout << \"String '\" << copy << \"' is \" << strlen(copy) << \" characters long.\" << std::endl;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "const char* hi = \"hi\";  // Not putting the const here triggers a warning.\n",
    "strncpy(copy, hi, strlen(hi));\n",
    "copy[strlen(hi)] = '\\0';  // Don't forget to terminate the string!\n",
    "std::cout << \"String '\" << copy << \"' is \" << strlen(copy) << \" characters long.\" << std::endl;\n"
576
577
578
579
580
581
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
582
    "There are several other functions related to historical strings; for more information, do not hesitate to consult [this reference page](http://www.cplusplus.com/reference/cstring/)."
583
584
585
586
587
588
589
590
591
592
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### std::string\n",
    "\n",
    "In modern C++, rather than bothering with character tables\n",
    "which come from the C language, it's easier to use the type `std::string`, provided\n",
593
    "through the standard language library, that provides a much simpler syntax:"
594
595
596
597
   ]
  },
  {
   "cell_type": "code",
598
599
600
   "execution_count": null,
   "metadata": {},
   "outputs": [],
601
602
603
604
605
   "source": [
    "#include <iostream>\n",
    "#include <cstring>  // For strlen\n",
    "#include <string>   // For std::string\n",
    "\n",
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
    "const char* hello_str = \"hello\";\n",
    "std::string hello = hello_str;\n",
    "std::string hi(\"hi\");\n",
    "std::string copy {};"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "copy = hello; // please notice affectation is much more straightforward\n",
    "std::cout << \"String '\" << copy << \"' is \" << copy.length() << \" characters long.\" << std::endl;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "const char* copy_str = copy.data();  // Returns a classic C-string (from C++11 onward)\n",
    "std::cout << \"String '\" << copy_str << \"' is \" << strlen(copy_str) << \" characters long.\" << std::endl;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "const char* old_copy_str = &copy[0];  // Same before C++11... \n",
    "std::cout << \"String '\" << old_copy_str << \"' is \" << strlen(old_copy_str) << \" characters long.\" << std::endl;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "std::string dynamic {\"dynamic std::string\"};\n",
    "std::cout << \"String '\" << dynamic << \"' is \" << dynamic.length() << \" characters long.\" << std::endl;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dynamic = \"std::string is dynamical and flexible\";\n",
    "std::cout << \"String '\" << dynamic << \"' is \" << dynamic.length() << \" characters long.\" << std::endl;"
660
661
   ]
  },
662
663
664
665
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
666
    "If needed (for instance to interact with a C library) you may access to the underlying table with `c_str()` or `data()` (both are interchangeable):"
667
668
669
670
671
672
673
674
675
676
677
678
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#include <string>\n",
    "{\n",
    "    std::string cplusplus_string(\"C++ string!\");\n",
    "    \n",
679
680
    "    const char* c_string = cplusplus_string.c_str();\n",
    "    const char* c_string_2 = cplusplus_string.data();\n",
681
682
683
684
685
686
687
688
689
690
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `const` here is important: you may access the content but should not modify it; this functionality is provided for read-only access."
   ]
  },
691
692
693
694
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
695
    "FYI, C++17 introduced [std::string_view](https://en.cppreference.com/w/cpp/header/string_view) which is more efficient than `std::string` for some operations (it is presented [in appendix](../7-Appendix/StringView.ipynb) but if it's your first reading it's a bit early to tackle it now)."
696
697
698
699
700
701
702
703
704
705
706
707
708
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Renaming types\n",
    "\n",
    "Sometimes it may be handy to rename a type, for instance if you want to be able to change easily throughout the code the numeric precision to use. Historical syntax (up to C++ 11 and still valid) was `typedef`:"
   ]
  },
  {
   "cell_type": "code",
709
710
711
   "execution_count": null,
   "metadata": {},
   "outputs": [],
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
   "source": [
    "#include <iostream>\n",
    "#include <iomanip>  // For std::setprecision\n",
    "\n",
    "{\n",
    "    typedef double real; // notice the ordering: new typename comes after its value\n",
    "    \n",
    "    real radius {1.};\n",
    "    real area = 3.1415926535897932385 * radius * radius;\n",
    "    std::cout <<\"Area = \" << std::setprecision(15) << area << std::endl;\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
729
    "In more modern C++ (C++11 and above), another syntax relying on `using` keyword was introduced; it is advised to use it as this syntax is more powerful in some contexts (see later with templates...):"
730
731
732
733
   ]
  },
  {
   "cell_type": "code",
734
735
736
   "execution_count": null,
   "metadata": {},
   "outputs": [],
737
738
739
740
741
   "source": [
    "#include <iostream>\n",
    "#include <iomanip>  // For std::setprecision\n",
    "\n",
    "{\n",
742
743
    "    using real = float; // notice the ordering: more in line with was we're accustomed to when \n",
    "                        // initialising variables.\n",
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
    "\n",
    "    real radius {1.};\n",
    "    real area = 3.1415926535897932385 * radius * radius;\n",
    "    std::cout <<\"Area = \" << std::setprecision(15) << area << std::endl;\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `decltype` and `auto`\n",
    "\n",
    "C++ 11 introduced new keywords that are very handy to deal with types:\n",
    "\n",
759
760
    "* `decltype` which is able to determine **at compile time** the underlying type of a variable.\n",
    "* `auto` which determines automatically **at compile time** the type of an expression."
761
762
763
764
   ]
  },
  {
   "cell_type": "code",
765
   "execution_count": null,
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
   "metadata": {},
   "outputs": [],
   "source": [
    "#include <vector>\n",
    "\n",
    "{\n",
    "    auto i = 5; // i is here an int.    \n",
    "    auto j = 5u; // j is an unsigned int\n",
    "    \n",
    "    decltype(j) k; // decltype(j) is interpreted by the compiler as an unsigned int.\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
783
    "On such trivial examples it might not seem much, but in practice it might prove incredibly useful. Consider for instance the following C++03 code (the details don't matter: we'll deal with `std::vector` in a [later notebook](../5-UsefulConceptsAndSTL/3-Containers.ipynb)):"
784
785
786
787
   ]
  },
  {
   "cell_type": "code",
788
789
790
   "execution_count": null,
   "metadata": {},
   "outputs": [],
791
   "source": [
792
    "#include <vector>\n",
793
794
795
    "#include <iostream>\n",
    "\n",
    "{\n",
796
797
798
799
800
801
802
803
804
805
806
807
    "    // C++ 03 initialization of a std::vector\n",
    "    std::vector<unsigned int> primes;\n",
    "    primes.push_back(2);\n",
    "    primes.push_back(3);\n",
    "    primes.push_back(5);\n",
    "    primes.push_back(7);    \n",
    "    primes.push_back(11);\n",
    "    primes.push_back(13);    \n",
    "    primes.push_back(17);        \n",
    "    primes.push_back(19);            \n",
    "\n",
    "    for (std::vector<unsigned int>::const_iterator it = primes.cbegin();\n",
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
    "          it != primes.cend();\n",
    "          ++it)\n",
    "     {\n",
    "         std::cout << *it << \" is prime.\" << std::endl;\n",
    "     }\n",
    "}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It's very verbosy; we could of course use alias:"
   ]
  },
  {
   "cell_type": "code",
825
   "execution_count": null,
826
827
828
   "metadata": {
    "scrolled": true
   },
829
   "outputs": [],
830
831
832
833
834
   "source": [
    "#include <vector>\n",
    "#include <iostream>\n",
    "\n",
    "{\n",
835
    "     std::vector<unsigned int> primes { 2, 3, 5, 7, 11, 13, 17, 19 }; // I'm cheating this time with C++ 11 notation...\n",
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
    "     using iterator = std::vector<unsigned int>::const_iterator;\n",
    "    \n",
    "     for (iterator it = primes.cbegin();\n",
    "          it != primes.cend();\n",
    "          ++it)\n",
    "     {\n",
    "         std::cout << *it << \" is prime.\" << std::endl;\n",
    "     }\n",
    "}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But with `decltype` we may write instead:"
   ]
  },
  {
   "cell_type": "code",
856
857
858
   "execution_count": null,
   "metadata": {},
   "outputs": [],
859
860
861
862
863
864
865
866
867
868
869
870
871
   "source": [
    "#include <vector>\n",
    "#include <iostream>\n",
    "\n",
    "{\n",
    "     std::vector<unsigned int> primes { 2, 3, 5, 7, 11, 13, 17, 19 }; // I'm cheating: it's C++ 11 notation...\n",
    "    \n",
    "     for (decltype(primes.cbegin()) it = primes.cbegin();\n",
    "          it != primes.cend();\n",
    "          ++it)\n",
    "     {\n",
    "         std::cout << *it << \" is prime.\" << std::endl;\n",
    "     }\n",
872
873
874
875
876
877
878
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
879
    "or even better:"
880
881
882
883
   ]
  },
  {
   "cell_type": "code",
884
885
886
   "execution_count": null,
   "metadata": {},
   "outputs": [],
887
   "source": [
888
    "#include <vector>\n",
889
890
891
    "#include <iostream>\n",
    "\n",
    "{\n",
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
    "     std::vector<unsigned int> primes { 2, 3, 5, 7, 11, 13, 17, 19 }; // I'm cheating: it's C++ 11 notation...\n",
    "    \n",
    "     for (auto it = primes.cbegin();\n",
    "          it != primes.cend();\n",
    "          ++it)\n",
    "     {\n",
    "         std::cout << *it << \" is prime.\" << std::endl;\n",
    "     }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
907
    "That is not to say `decltype` is always inferior to `auto`: there are some cases in which decltype is invaluable (especially in metaprogramming, but it's mostly out of the scope of this lecture - we'll skim briefly over it in a later [notebook](../4-Templates/4-Metaprogramming.ipynb)). \n",
908
    "\n",
909
910
911
912
913
    "C++ 14 introduced a new one (poorly) called `decltype(auto)` which usefulness will be explained below:"
   ]
  },
  {
   "cell_type": "code",
914
915
916
   "execution_count": null,
   "metadata": {},
   "outputs": [],
917
918
919
920
   "source": [
    "#include <algorithm>\n",
    "#include <iostream>\n",
    "\n",
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
    "int i = 5;\n",
    "int& j = i;\n",
    "\n",
    "auto k = j;\n",
    "\n",
    "if (std::is_same<decltype(j), decltype(k)>())\n",
    "    std::cout << \"j and k are of the same type.\" << std::endl;\n",
    "else\n",
    "    std::cout << \"j and k are of different type.\" << std::endl;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "if (std::is_same<decltype(i), decltype(k)>())\n",
    "    std::cout << \"i and k are of the same type.\" << std::endl;\n",
    "else\n",
    "    std::cout << \"i and k are of different type.\" << std::endl;"
942
943
944
945
946
947
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
948
    "Despite the `auto k = j`, j and k don't share the same type! The reason for this is that `auto` loses information about pointers, reference or constness in the process...\n",
949
    "\n",
950
    "A way to circumvent this is `auto& k = j`.\n",
951
    "\n",
952
    "`decltype(auto)` was introduced to fill this hole: contrary to `auto` it retains all these informations:"
953
954
955
956
   ]
  },
  {
   "cell_type": "code",
957
958
959
   "execution_count": null,
   "metadata": {},
   "outputs": [],
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
   "source": [
    "#include <algorithm>\n",
    "#include <iostream>\n",
    "\n",
    "{\n",
    "    int i = 5;\n",
    "    int& j = i;\n",
    "    \n",
    "    decltype(auto) k = j;\n",
    "    \n",
    "    if (std::is_same<decltype(j), decltype(k)>())\n",
    "        std::cout << \"j and k are of the same type.\" << std::endl;\n",
    "    else\n",
    "        std::cout << \"j and k are of different type.\" << std::endl;\n",
    "}"
   ]
  },
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### `auto` and string litterals\n",
    "\n",
    "**Beware:** when you declare a string litterals with `auto`, the type deduction makes it a `const char*`, not a `std::string`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#include <algorithm>\n",
    "#include <string>\n",
    "#include <iostream>\n",
    "\n",
    "auto hello_str = \"Hello world\"; // declares a char*\n",
    "\n",
    "std::cout << \"Is 'hello_str' a const char*? \" << std::boolalpha << std::is_same<decltype(hello_str), const char*>() << std::endl;\n",
    "std::cout << \"Is 'hello_str' a std::string? \" << std::boolalpha << std::is_same<decltype(hello_str), std::string>() << std::endl;"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "C++ 14 introduced a suffix to facilitate declaration of a `std::string` from a string litterals... but which requires to add a specific `using namespace` first (we will see that those are in a [much later notebook](../6-InRealEnvironment/5-Namespace.ipynb)). "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#include <string>\n",
    "\n",
    "using namespace std::string_literals;\n",
    "\n",
    "std::string hello_string(\"Hello world\"); // the 'classic' way to define a std::string\n",
    "auto hello_str = \"Hello world\"s;  // declares a std::string - requires first the using namespace directive\n",
    "\n",
    "std::cout << \"Is 'hello_string' a const char*? \" << std::boolalpha << std::is_same<decltype(hello_string), const char*>() << std::endl;\n",
    "std::cout << \"Is 'hello_string' a std::string? \" << std::boolalpha << std::is_same<decltype(hello_string), std::string>() << std::endl;\n",
    "\n",
    "std::cout << \"Is 'hello_str' a const char*? \" << std::boolalpha << std::is_same<decltype(hello_str), const char*>() << std::endl;\n",
    "std::cout << \"Is 'hello_str' a std::string? \" << std::boolalpha << std::is_same<decltype(hello_str), std::string>() << std::endl;"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Not sure it it is entirely worth it (maybe when you define loads of `std::string` is a same file?) but you may see that syntax in an existing program."
   ]
  },
1036
1037
1038
1039
1040
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
1041
    "© _CNRS 2016_ - _Inria 2018-2021_   \n",
1042
1043
    "_This notebook is an adaptation of a lecture prepared by David Chamont (CNRS) under the terms of the licence [Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/)_  \n",
    "_The present version has been written by Sébastien Gilles and Vincent Rouvreau (Inria)_\n",
1044
1045
    "\n"
   ]
1046
1047
1048
1049
1050
1051
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "C++17",
   "language": "C++17",
1052
   "name": "xcpp17"
1053
1054
1055
1056
1057
1058
  },
  "language_info": {
   "codemirror_mode": "text/x-c++src",
   "file_extension": ".cpp",
   "mimetype": "text/x-c++src",
   "name": "c++",
1059
   "version": "17"
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
  },
  "latex_envs": {
   "LaTeX_envs_menu_present": true,
   "autoclose": false,
   "autocomplete": true,
   "bibliofile": "biblio.bib",
   "cite_by": "apalike",
   "current_citInitial": 1,
   "eqLabelWithNumbers": true,
   "eqNumInitial": 1,
   "hotkeys": {
    "equation": "Ctrl-E",
    "itemize": "Ctrl-I"
   },
   "labels_anchors": false,
   "latex_user_defs": false,
   "report_style_numbering": false,
   "user_envs_cfg": false
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": true,
   "title_cell": "Table of contents",
   "title_sidebar": "Contents",
   "toc_cell": true,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": true
1091
1092
1093
  }
 },
 "nbformat": 4,
1094
 "nbformat_minor": 4
1095
}