C++20: The small things Version 1.3 Timur Doumler @timur_audio - - PowerPoint PPT Presentation

c 20 the small things
SMART_READER_LITE
LIVE PREVIEW

C++20: The small things Version 1.3 Timur Doumler @timur_audio - - PowerPoint PPT Presentation

C++20: The small things Version 1.3 Timur Doumler @timur_audio MeetingC++ 14 November 2019 we are here 1 Initialisation 2 Structured bindings 3 Lambdas 4 Templates 5 constexpr 6 Miscellaneous 11 1 Initialisation 2


slide-1
SLIDE 1

Timur Doumler

MeetingC++ 14 November 2019

C++20: The small things

@timur_audio Version 1.3

slide-2
SLIDE 2
slide-3
SLIDE 3
slide-4
SLIDE 4
slide-5
SLIDE 5

we are here

slide-6
SLIDE 6
slide-7
SLIDE 7
slide-8
SLIDE 8
slide-9
SLIDE 9
slide-10
SLIDE 10
slide-11
SLIDE 11

11

1 Initialisation 2 Structured bindings 3 Lambdas 4 Templates 5 constexpr 6 Miscellaneous

slide-12
SLIDE 12

12

1 Initialisation 2 Structured bindings 3 Lambdas 4 Templates 5 constexpr 6 Miscellaneous

slide-13
SLIDE 13

Aggregates

13

struct Widget { int a; bool b; int c; };

slide-14
SLIDE 14

Aggregates

14

struct Widget { int a; bool b; int c; }; int main() { Widget widget = {3, true}; }

slide-15
SLIDE 15

Designated initialisers

15

struct Widget { int a; bool b; int c; }; int main() { Widget widget{.a = 3, .c = 7}; }

slide-16
SLIDE 16

16

struct Widget { int a; bool b; int c; }; int main() { Widget widget{.a = 3, .c = 7}; }

Only for aggregate types. C compatibility feature. Works like in C99, except:

  • not out-of-order

Widget widget{.c = 7, .a = 3} // Error

  • not nested

Widget widget{.c.e = 7} // Error

  • not mixed with regular initialisers

Widget widget{.a = 3, 7} // Error

  • not with arrays

int arr[3]{.[1] = 7} // Error

Designated initialisers

slide-17
SLIDE 17

17

slide-18
SLIDE 18

18

Aggregates can no longer declare constructors

slide-19
SLIDE 19

19

struct Widget { Widget() = delete; }; Widget w1; // Error

Aggregates can no longer declare constructors

slide-20
SLIDE 20

20

struct Widget { Widget() = delete; }; Widget w1; // Error Widget w2{}; // OK in C++17!

Aggregates can no longer declare constructors

slide-21
SLIDE 21

21

struct Widget { Widget() = delete; }; Widget w1; // Error Widget w2{}; // OK in C++17! Will be error in C++20

Aggregates can no longer declare constructors

slide-22
SLIDE 22
  • Does not work with macros:

22

assert(Widget(2, 3)); // OK

C++17 problems with aggregate initialisation:

slide-23
SLIDE 23
  • Does not work with macros:

23

assert(Widget(2, 3)); // OK assert(Widget{2, 3}); // Error: this breaks the preprocessor :(

C++17 problems with aggregate initialisation:

slide-24
SLIDE 24
  • Does not work with macros:
  • Can’t do perfect forwarding in templates
  • can’t write emplace or make_unique that works for aggregates :(

24

assert(Widget(2, 3)); // OK assert(Widget{2, 3}); // Error: this breaks the preprocessor :(

C++17 problems with aggregate initialisation:

slide-25
SLIDE 25

25

struct Widget { int i; int j; }; Widget widget(1, 2); // will work in C++20!

C++20: Direct-initialisation of aggregates

slide-26
SLIDE 26

26

struct Widget { int i; int j; }; Widget widget(1, 2); // will work in C++20! int arr[3](0, 1, 2); // will work in C++20!

C++20: Direct-initialisation of aggregates

slide-27
SLIDE 27

27

struct Widget { int i; int j; }; Widget widget(1, 2); // will work in C++20! int arr[3](0, 1, 2); // will work in C++20!

So in C++20, (args) and {args} will do the same thing! Except:

  • () does not call std::initializer_list constructors
  • {} does not allow narrowing conversions

C++20: Direct-initialisation of aggregates

slide-28
SLIDE 28

28

struct Colour { Colour(int r, int g, int b) noexcept; };

constinit

slide-29
SLIDE 29

29

struct Colour { Colour(int r, int g, int b) noexcept; }; namespace Colours { const Colour red = {255, 0, 0}; }

constinit

slide-30
SLIDE 30

30

struct Colour { Colour(int r, int g, int b) noexcept; }; namespace Colours { const Colour red = {255, 0, 0}; // dynamic initialisation }

constinit

slide-31
SLIDE 31

31

struct Colour { Colour(int r, int g, int b) noexcept; }; namespace Colours { const Colour red = {255, 0, 0}; // dynamic initialisation } // -> initialisation order fiasco -> UB :(

constinit

slide-32
SLIDE 32

32

struct Colour { constexpr Colour(int r, int g, int b) noexcept; }; namespace Colours { constexpr Colour red = {255, 0, 0}; }

constinit

slide-33
SLIDE 33

33

struct Colour { constexpr Colour(int r, int g, int b) noexcept; }; namespace Colours { constexpr Colour red = {255, 0, 0}; // constant initialisation :) }

constinit

slide-34
SLIDE 34

struct Colour { constexpr Colour(int r, int g, int b) noexcept; }; namespace Colours { Colour backgroundColour = getBackgroundColour(); }

34

constinit

slide-35
SLIDE 35

struct Colour { constexpr Colour(int r, int g, int b) noexcept; }; namespace Colours { Colour backgroundColour = getBackgroundColour(); // const or dynamic init? }

35

constinit

slide-36
SLIDE 36

struct Colour { constexpr Colour(int r, int g, int b) noexcept; }; namespace Colours { constinit Colour backgroundColour = getBackgroundColour(); }

36

constinit

slide-37
SLIDE 37

37

constinit

struct Colour { constexpr Colour(int r, int g, int b) noexcept; }; namespace Colours { constinit Colour backgroundColour = getBackgroundColour(); } // ^^^^^^ only compiles if init happens at compile time :)

slide-38
SLIDE 38

Database getDatabase(); for (auto&& user : getDatabase().getUsers()) { registerUser(user); }

38

Range-based for with initialiser C++17

slide-39
SLIDE 39

Database getDatabase(); for (auto&& user : getDatabase().getUsers()) // maybe undefined behaviour! { registerUser(user); }

39

Range-based for with initialiser C++17

slide-40
SLIDE 40

Database getDatabase(); auto db = getDatabase(); for (auto&& user : db.getUsers()) { registerUser(user); }

40

Range-based for with initialiser C++17

slide-41
SLIDE 41

Database getDatabase(); { auto db = getDatabase(); for (auto&& user : db.getUsers()) { registerUser(user); } }

41

Range-based for with initialiser C++17

slide-42
SLIDE 42

42

Range-based for with initialiser

Database getDatabase(); for (auto db = getDatabase(); auto&& user : db.getUsers()) { registerUser(user); }

C++20

slide-43
SLIDE 43

43

1 Initialisation 2 Structured bindings 3 Lambdas 4 Templates 5 constexpr 6 Miscellaneous

slide-44
SLIDE 44

44

struct Widget { int i; bool b; }; auto [a, b] = getWidget();

slide-45
SLIDE 45

45

struct Widget { int i; bool b; }; auto [a, b] = getWidget(); static [a, b] = getWidget(); // Error in C++17 thread_local [a, b] = getWidget(); // Error in C++17

slide-46
SLIDE 46

46

struct Widget { int i; bool b; }; auto [a, b] = getWidget(); static [a, b] = getWidget(); // OK in C++20 thread_local [a, b] = getWidget(); // OK in C++20

slide-47
SLIDE 47

47

struct Widget { int i; bool b; }; auto [a, b] = getWidget(); auto f = [a]{ return a > 0; }; // Error in C++17: // capture ‘a’ does not name a variable

slide-48
SLIDE 48

48

struct Widget { int i; bool b; }; auto [a, b] = getWidget(); auto f = [a]{ return a > 0; }; // OK in C++20

slide-49
SLIDE 49

49

struct Widget { int i; bool b; }; auto [a, b] = getWidget(); auto f = [a]{ return a > 0; }; // OK in C++20 // copies ‘a’, not the whole object

slide-50
SLIDE 50

50

1 Initialisation 2 Structured bindings 3 Lambdas 4 Templates 5 constexpr 6 Miscellaneous

slide-51
SLIDE 51

template<class F, class... Args> auto delay_invoke(F f, Args... args) { return [f = std::move(f), ...args = std::move(args)]() -> decltype(auto) { return std::invoke(f, args...); }; }

51

C++20: pack expansion allowed in lambda init capture

slide-52
SLIDE 52

52

– Lambdas are allowed in unevaluated contexts – Lambdas (without captures) are default-constructible and assignable

More C++20 lambda features:

slide-53
SLIDE 53

53

– Lambdas are allowed in unevaluated contexts – Lambdas (without captures) are default-constructible and assignable

More C++20 lambda features:

decltype([]{})

slide-54
SLIDE 54

54

– Lambdas are allowed in unevaluated contexts – Lambdas (without captures) are default-constructible and assignable

More C++20 lambda features:

decltype([]{}) f;

slide-55
SLIDE 55

55

– Lambdas are allowed in unevaluated contexts – Lambdas (without captures) are default-constructible and assignable

More C++20 lambda features:

class Widget { decltype([]{}) f; };

slide-56
SLIDE 56

56

template <typename T> using MyPtr = std::unique_ptr< T, decltype([](T* t) { myDeleter(t); })>; MyPtr<Widget> ptr;

slide-57
SLIDE 57

57

template <typename T> using MyPtr = std::unique_ptr< T, decltype([](T* t) { myDeleter(t); })>; MyPtr<Widget> ptr; using WidgetSet = std::set< Widget, decltype([](Widget& lhs, Widget& rhs) { return lhs.x < rhs.x; })>; WidgetSet widgets;

slide-58
SLIDE 58

58

Generic lambdas / functions

auto f = [](auto a){ return a * a; };

slide-59
SLIDE 59

59

auto f = [](auto a){ return a * a; }; auto f(auto a) { // Generic *functions* – OK since C++20 :) return a * a; }

Generic lambdas / functions

slide-60
SLIDE 60

60

template <typename T> void f(std::vector<T> vector) { // ... }

Generic lambdas / functions

slide-61
SLIDE 61

61

template <typename T> void f(std::vector<T> vector) { // ... } // C++20: auto f = []<typename T>(std::vector<T> vector) { // ... };

Generic lambdas / functions

slide-62
SLIDE 62

62

1 Initialisation 2 Structured bindings 3 Lambdas 4 Templates 5 constexpr 6 Miscellaneous

slide-63
SLIDE 63

63

Non-type template parameters (NTTPs)

slide-64
SLIDE 64

64

Non-type template parameters (NTTPs)

template <int size> struct Widget { std::array<int, size> a; };

slide-65
SLIDE 65

65

Non-type template parameters (NTTPs)

template <int size> struct Widget { std::array<int, size> a; };

slide-66
SLIDE 66

66

C++20: floating-point NTTPs

template <double x> struct Filter { std::array<double, 2> coefficients = {x, 0.5 * x * x}; // stuff... };

slide-67
SLIDE 67

67

C++20: class-type NTTPs

struct Coefficients { double x; double y; };

slide-68
SLIDE 68

68

C++20: class-type NTTPs

struct Coefficients { double x; double y; }; template <Coefficients coeffs> struct Filter { // stuff :) };

slide-69
SLIDE 69

69

C++20: class-type NTTPs

struct Coefficients { double x; double y; }; template <Coefficients coeffs> struct Filter { // stuff :) }; constexpr Filter<Coefficients{1, 0.125}> f;

slide-70
SLIDE 70

70

CTAD

slide-71
SLIDE 71

71

CTAD

std::vector v = {1, 2, 3}; // std::vector<int>

slide-72
SLIDE 72

72

CTAD

std::vector v = {1, 2, 3}; // std::vector<int> std::tuple t = {42, 0.5, true}; // std::tuple<int, double, bool>

slide-73
SLIDE 73

73

CTAD

std::vector v = {1, 2, 3}; // std::vector<int> std::tuple t = {42, 0.5, true}; // std::tuple<int, double, bool> std::scoped_lock lock(rtmutex); // std::scoped_lock<std::recursive_timed_mutex>

slide-74
SLIDE 74

74

C++20 adds:

– CTAD for aggregates – CTAD for alias templates

slide-75
SLIDE 75

75

template <typename T, typename U> struct aggr_pair { T t; U u; }; aggr_pair p = {1, true}; // Error: no deduction candidate found

C++17

slide-76
SLIDE 76

76

template <typename T, typename U> struct aggr_pair { T t; U u; }; template <typename T, typename U> aggr_pair(T, U) -> aggr_pair<T, U>; aggr_pair p = {1, true}; // OK

C++17

slide-77
SLIDE 77

77

template <typename T, typename U> struct aggr_pair { T t; U u; }; template <typename T, typename U> aggr_pair(T, U) -> aggr_pair<T, U>; aggr_pair p = {1, true}; // OK

C++17 C++20

template <typename T, typename U> struct aggr_pair { T t; U u; }; aggr_pair p = {1, true}; // OK

slide-78
SLIDE 78

78

template<typename... Bases> struct overloaded : Bases... { using Bases::operator()...; };

C++17

slide-79
SLIDE 79

79

template<typename... Bases> struct overloaded : Bases... { using Bases::operator()...; }; template<typename... Bases>

  • verloaded(Bases...) -> overloaded<Bases...>;

C++17

slide-80
SLIDE 80

80

template<typename... Bases> struct overloaded : Bases... { using Bases::operator()...; }; template<typename... Bases>

  • verloaded(Bases...) -> overloaded<Bases...>;
  • verloaded printer = {

[](auto arg) { std::cout << arg << ' '; }, [](double arg) { std::cout << std::fixed << arg << ' '; }, [](const char* arg) { std::cout << std::quoted(arg) << ' '; } };

C++17

slide-81
SLIDE 81

template<typename... Bases> struct overloaded : Bases... { using Bases::operator()...; }; template<typename... Bases>

  • verloaded(Bases...) -> overloaded<Bases...>;
  • verloaded printer = {

[](auto arg) { std::cout << arg << ' '; }, [](double arg) { std::cout << std::fixed << arg << ' '; }, [](const char* arg) { std::cout << std::quoted(arg) << ' '; } }; int main() { printer("Hello, World!"); }

81

C++17

slide-82
SLIDE 82

82

template<typename... Bases> struct overloaded : Bases... { using Bases::operator()...; }; template<typename... Bases>

  • verloaded(Bases...) -> overloaded<Bases...>;
  • verloaded printer = {

[](auto arg) { std::cout << arg << ' '; }, [](double arg) { std::cout << std::fixed << arg << ' '; }, [](const char* arg) { std::cout << std::quoted(arg) << ' '; } }; int main() { printer("Hello, World!"); }

C++17

slide-83
SLIDE 83

template<typename... Bases> struct overloaded : Bases... { using Bases::operator()...; };

  • verloaded printer = {

[](auto arg) { std::cout << arg << ' '; }, [](double arg) { std::cout << std::fixed << arg << ' '; }, [](const char* arg) { std::cout << std::quoted(arg) << ' '; } }; int main() { printer("Hello, World!"); }

83

C++20

slide-84
SLIDE 84

84

namespace pmr { template <class T> using vector = std::vector<T, std::pmr::polymorphic_allocator<T>>; }

C++17

std::pmr::vector<int> v{1, 2, 3};

slide-85
SLIDE 85

85

namespace pmr { template <class T> using vector = std::vector<T, std::pmr::polymorphic_allocator<T>>; }

C++17 C++20

std::pmr::vector<int> v{1, 2, 3}; std::pmr::vector v{1, 2, 3};

slide-86
SLIDE 86

86

1 Initialisation 2 Structured bindings 3 Lambdas 4 Templates 5 constexpr 6 Miscellaneous

slide-87
SLIDE 87

87

In C++20, in a constexpr function you can:

– have a try-block – have an unevaluated asm block – use a union – call virtual functions – dynamic_cast and typeid – new and delete

slide-88
SLIDE 88

88

Daveed Vandevoorde “C++ Constants” C++Now 2019 keynote Louis Dionne "Compile-time programming and reflection in C++20 and beyond” CppCon 2018 talk

slide-89
SLIDE 89

89

“running” code at compile time

int square(int i) { return i * i; }

slide-90
SLIDE 90

90

“running” code at compile time

constexpr int square(int i) { return i * i; } square(3); // compile time square(x); // runtime

slide-91
SLIDE 91

consteval int square(int i) { return i * i; } square(3); // compile time square(x); // Error - x is not a compile-time constant!

91

“running” code at compile time

slide-92
SLIDE 92

92

compile time or runtime?

int square(int i) { return __magic_fast_square(i); // contains runtime magic } square(3); // runtime, fast magic square(x); // runtime, fast magic

slide-93
SLIDE 93

93

compile time or runtime?

constexpr int square(int i) { return i * i; } square(3); // compile time square(x); // runtime, no fast magic :(

slide-94
SLIDE 94

94

compile time or runtime?

constexpr int square(int i) { if (std::is_constant_evaluated()) { return i * i; } else { return __magic_fast_square(i); } } square(3); // compile time square(x); // runtime, fast magic :)

slide-95
SLIDE 95

constexpr int square(int i) { if (std::is_constant_evaluated()) { return i * i; } else { return __magic_fast_square(i); } } square(3); // compile time square(x); // runtime, fast magic :)

95

compile time or runtime?

slide-96
SLIDE 96

96

1 Initialisation 2 Structured bindings 3 Lambdas 4 Templates 5 constexpr 6 Miscellaneous

slide-97
SLIDE 97

97

template <typename Container> auto findFirstValid(const Container& c) -> Container::iterator { return std::find_if(c.begin(), c.end(), [](auto elem){ return elem.is_valid(); }); }

slide-98
SLIDE 98

98

template <typename Container> auto findFirstValid(const Container& c) -> Container::const_iterator { return std::find_if(c.begin(), c.end(), [](auto elem){ return elem.is_valid(); }); } // Error: missing 'typename' prior to dependent type name ‘Container::const_iterator'

slide-99
SLIDE 99

99

template <typename Container> auto findFirstValid(const Container& c) -> Container::const_iterator { return std::find_if(c.begin(), c.end(), [](auto elem){ return elem.is_valid(); }); } // OK in C++20 :)

slide-100
SLIDE 100

100

New attributes in C++20

slide-101
SLIDE 101

101

– [[likely]], [[unlikely]]

New attributes in C++20

slide-102
SLIDE 102

102

– [[likely]], [[unlikely]] – [[no_unique_address]]

New attributes in C++20

slide-103
SLIDE 103

103

– [[likely]], [[unlikely]] – [[no_unique_address]] – [[nodiscard]] on constructors

New attributes in C++20

slide-104
SLIDE 104

104

– [[likely]], [[unlikely]] – [[no_unique_address]] – [[nodiscard]] on constructors – [[nodiscard(“can have a message”)]]

New attributes in C++20

slide-105
SLIDE 105

enum class rgba_color_channel { red, green, blue, alpha };

105

Using enum

slide-106
SLIDE 106

enum class rgba_color_channel { red, green, blue, alpha }; std::string_view to_string(rgba_color_channel channel) { switch (channel) { case rgba_color_channel::red: return "red"; case rgba_color_channel::green: return "green"; case rgba_color_channel::blue: return "blue"; case rgba_color_channel::alpha: return "alpha"; } }

106

Using enum

slide-107
SLIDE 107

107

Using enum

enum class rgba_color_channel { red, green, blue, alpha }; std::string_view to_string(rgba_color_channel channel) { switch (channel) { case rgba_color_channel::red: return "red"; case rgba_color_channel::green: return "green"; case rgba_color_channel::blue: return "blue"; case rgba_color_channel::alpha: return "alpha"; } }

slide-108
SLIDE 108

enum class rgba_color_channel { red, green, blue, alpha }; std::string_view to_string(rgba_color_channel channel) { switch (channel) { using enum rgba_color_channel; case red: return "red"; case green: return "green"; case blue: return "blue"; case alpha: return "alpha"; } }

108

Using enum

slide-109
SLIDE 109

enum class Suit { diamonds, hearts, spades, clubs }; class Card { using enum Suit; Suit suit = spades; };

109

Using enum

slide-110
SLIDE 110

110

Built-in UTF-8 char type

int main() { const char* str = u8"🌉❤"; // C++17... }

slide-111
SLIDE 111

111

Built-in UTF-8 char type

int main() { const char* str = u8"🌉❤"; // compile error in C++20! }

slide-112
SLIDE 112

112

Built-in UTF-8 char type

int main() { const char8_t* str = u8"🌉❤"; }

slide-113
SLIDE 113

we are here

slide-114
SLIDE 114

Timur Doumler

MeetingC++ 14 November 2019

C++20: The small things

@timur_audio Version 1.3