0% found this document useful (0 votes)
4 views79 pages

C 20 The Complete Guide Nicolai M. Josuttis Install Download

C++20 - The Complete Guide by Nicolai M. Josuttis is a comprehensive resource published on October 30, 2022, covering the features and functionalities of C++20. The book includes detailed explanations, examples, and practical applications of new concepts, types, and programming techniques introduced in C++20. It is available for purchase on Leanpub and is dedicated to the victims of the war in Ukraine.

Uploaded by

zghwkmwhnl028
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views79 pages

C 20 The Complete Guide Nicolai M. Josuttis Install Download

C++20 - The Complete Guide by Nicolai M. Josuttis is a comprehensive resource published on October 30, 2022, covering the features and functionalities of C++20. The book includes detailed explanations, examples, and practical applications of new concepts, types, and programming techniques introduced in C++20. It is available for purchase on Leanpub and is dedicated to the victims of the war in Ukraine.

Uploaded by

zghwkmwhnl028
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 79

C 20 The Complete Guide Nicolai M.

Josuttis
install download

https://ebookmeta.com/product/c-20-the-complete-guide-nicolai-m-
josuttis/

Download more ebook from https://ebookmeta.com


We believe these products will be a great fit for you. Click
the link to download now, or visit ebookmeta.com
to discover even more!

C 20 The Complete Guide 1 (2022-10-30) Edition Nicolai


M. Josuttis

https://ebookmeta.com/product/c-20-the-complete-
guide-1-2022-10-30-edition-nicolai-m-josuttis/

C Move Semantics The Complete Guide 1st Edition Nicolai


M. Josuttis

https://ebookmeta.com/product/c-move-semantics-the-complete-
guide-1st-edition-nicolai-m-josuttis/

C 17 The Complete Guide 1 (2022-01-11) Edition Nicolai


M. Josuttis

https://ebookmeta.com/product/c-17-the-complete-
guide-1-2022-01-11-edition-nicolai-m-josuttis/

Of Fire and Stars 1st Edition Audrey Coulthurst

https://ebookmeta.com/product/of-fire-and-stars-1st-edition-
audrey-coulthurst/
Genetics Essentials Concepts and Connections 5th
Edition Benjamin Pierce

https://ebookmeta.com/product/genetics-essentials-concepts-and-
connections-5th-edition-benjamin-pierce/

SunriseSunset Solargraphs from Plum Creek Wittliff


Collections Photography Series Bill Wittliff

https://ebookmeta.com/product/sunrisesunset-solargraphs-from-
plum-creek-wittliff-collections-photography-series-bill-wittliff/

Mountain Man's Captive: An OTT Mountain Man Romance


(Men of Maple Mountain Book 2) 1st Edition Sadie King

https://ebookmeta.com/product/mountain-mans-captive-an-ott-
mountain-man-romance-men-of-maple-mountain-book-2-1st-edition-
sadie-king/

Surprise Kissmas My Roommate Guy Next Door Romance All


I Want for Kissmas 1st Edition Brynn Hale

https://ebookmeta.com/product/surprise-kissmas-my-roommate-guy-
next-door-romance-all-i-want-for-kissmas-1st-edition-brynn-hale/

Reality 1st Edition Layla Heart

https://ebookmeta.com/product/reality-1st-edition-layla-heart/
Garrett s Obsession A Curvy Girl Instalove Romance 1st
Edition Loni Nichole

https://ebookmeta.com/product/garrett-s-obsession-a-curvy-girl-
instalove-romance-1st-edition-loni-nichole-2/
Nicolai M. Josuttis

C++20 - The Complete Guide

First Edition
To the nation of Ukraine
and all the children, women, men, and soldiers
killed in a terrible war driven by a Russian dictator

C++20 - The Complete Guide


First Edition

Nicolai M. Josuttis

This version was published on 2022-10-30.

© 2022 by Nicolai Josuttis. All rights reserved.


This publication is protected by copyright, and permission must be obtained from the author prior
to any prohibited reproduction, storage in a retrieval system, or transmission in any form or by any
means, electronic, mechanical, photocopying, recording, or likewise.

This book was typeset by Nicolai M. Josuttis using the LATEX document processing system.

This book is for sale at http://leanpub.com/cpp20.


This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book, and build traction once
you do.
Contents
Preface xix
An Experiment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xix
Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xix
Versions of This Book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xx

About This Book xxiii


What You Should Know Before Reading This Book . . . . . . . . . . . . . . . . . . . . . . . . . . xxiii
Overall Structure of the Book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxiii
How to Read This Book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxiv
The Way I Implement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxiv
The C++ Standards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxv
Example Code and Additional Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxv
Feedback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxvi

1 Comparisons and Operator <=> 1


1.1 Motivation for Operator<=> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.1 Defining Comparison Operators Before C++20 . . . . . . . . . . . . . . . . . . . . . 1
1.1.2 Defining Comparison Operators Since C++20 . . . . . . . . . . . . . . . . . . . . . . 4
1.2 Defining and Using Comparisons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2.1 Using Operator<=> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2.2 Comparison Category Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2.3 Using Comparison Categories with operator<=> . . . . . . . . . . . . . . . . . . . . 9
1.2.4 Calling Operator <=> Directly . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.2.5 Dealing with Multiple Ordering Criteria . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.3 Defining operator<=> and operator== . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.3.1 Defaulted operator== and operator<=> . . . . . . . . . . . . . . . . . . . . . . . . 14

iii
iv Contents

1.3.2 Defaulted operator<=> Implies Defaulted operator== . . . . . . . . . . . . . . . 14


1.3.3 Implementation of the Defaulted operator<=> . . . . . . . . . . . . . . . . . . . . . 16
1.4 Overload Resolution with Rewritten Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.5 Using Operator <=> in Generic Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.5.1 compare_three_way . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.5.2 Algorithm lexicographical_compare_three_way() . . . . . . . . . . . . . . . 19
1.6 Compatibility Issues with the Comparison Operators . . . . . . . . . . . . . . . . . . . . . . . 21
1.6.1 Delegating Free-Standing Comparison Operators . . . . . . . . . . . . . . . . . . . . 21
1.6.2 Inheritance with Protected Members . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
1.7 Afternotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

2 Placeholder Types for Function Parameters 25


2.1 auto for Parameters of Ordinary Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.1.1 auto for Parameters of Member Functions . . . . . . . . . . . . . . . . . . . . . . . . 26
2.2 Using auto for Parameters in Practice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.2.1 Deferred Type Checks with auto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.2.2 auto Functions versus Lambdas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
2.3 auto for Parameters in Detail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.3.1 Basic Constraints for auto Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.3.2 Combining Template and auto Parameters . . . . . . . . . . . . . . . . . . . . . . . . 31
2.4 Afternotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

3 Concepts, Requirements, and Constraints 33


3.1 Motivating Example of Concepts and Requirements . . . . . . . . . . . . . . . . . . . . . . . . 33
3.1.1 Improving the Template Step by Step . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.1.2 A Complete Example with Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3.2 Where Constraints and Concepts Can Be Used . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
3.2.1 Constraining Alias Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
3.2.2 Constraining Variable Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
3.2.3 Constraining Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
3.2.4 Constraining Non-Type Template Parameters . . . . . . . . . . . . . . . . . . . . . . . 43
3.3 Typical Applications of Concepts and Constraints in Practice . . . . . . . . . . . . . . . . . . 44
3.3.1 Using Concepts to Understand Code and Error Messages . . . . . . . . . . . . . . . 44
3.3.2 Using Concepts to Disable Generic Code . . . . . . . . . . . . . . . . . . . . . . . . . 47
3.3.3 Using Requirements to Call Different Functions . . . . . . . . . . . . . . . . . . . . . 51
Contents v

3.3.4 The Example as a Whole . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56


3.3.5 Former Workarounds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
3.4 Semantic Constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.4.1 Examples of Semantic Constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.5 Design Guidelines for Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
3.5.1 Concepts Should Group Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
3.5.2 Define Concepts with Care . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
3.5.3 Concepts versus Type Traits and Boolean Expressions . . . . . . . . . . . . . . . . . 62
3.6 Afternotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

4 Concepts, Requirements, and Constraints in Detail 65


4.1 Constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.2 requires Clauses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
4.2.1 Using && and || in requires Clauses . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
4.3 Ad-hoc Boolean Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
4.4 requires Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
4.4.1 Simple Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
4.4.2 Type Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
4.4.3 Compound Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
4.4.4 Nested Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
4.5 Concepts in Detail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
4.5.1 Defining Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
4.5.2 Special Abilities of Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
4.5.3 Concepts for Non-Type Template Parameters . . . . . . . . . . . . . . . . . . . . . . . 76
4.6 Using Concepts as Type Constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
4.7 Subsuming Constraints with Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
4.7.1 Indirect Subsumptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
4.7.2 Defining Commutative Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83

5 Standard Concepts in Detail 87


5.1 Overview of All Standard Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
5.1.1 Header Files and Namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
5.1.2 Standard Concepts Subsume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
5.2 Language-Related Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
5.2.1 Arithmetic Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
vi Contents

5.2.2 Object Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91


5.2.3 Concepts for Relationships between Types . . . . . . . . . . . . . . . . . . . . . . . . 93
5.2.4 Comparison Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
5.3 Concepts for Iterators and Ranges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
5.3.1 Concepts for Ranges and Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
5.3.2 Concepts for Pointer-Like Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
5.3.3 Concepts for Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
5.3.4 Iterator Concepts for Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
5.4 Concepts for Callables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
5.4.1 Basic Concepts for Callables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
5.4.2 Concepts for Callables Used by Iterators . . . . . . . . . . . . . . . . . . . . . . . . . 109
5.5 Auxiliary Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
5.5.1 Concepts for Specific Type Attributes . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
5.5.2 Concepts for Incrementable Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

6 Ranges and Views 115


6.1 A Tour of Ranges and Views Using Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
6.1.1 Passing Containers to Algorithms as Ranges . . . . . . . . . . . . . . . . . . . . . . . 116
6.1.2 Constraints and Utilities for Ranges . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
6.1.3 Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
6.1.4 Sentinels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
6.1.5 Range Definitions with Sentinels and Counts . . . . . . . . . . . . . . . . . . . . . . . 129
6.1.6 Projections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
6.1.7 Utilities for Implementing Code for Ranges . . . . . . . . . . . . . . . . . . . . . . . . 133
6.1.8 Limitations and Drawbacks of Ranges . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
6.2 Borrowed Iterators and Ranges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
6.2.1 Borrowed Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
6.2.2 Borrowed Ranges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
6.3 Using Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
6.3.1 Views on Ranges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
6.3.2 Lazy Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
6.3.3 Caching in Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
6.3.4 Performance Issues with Filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
6.4 Views on Ranges That Are Destroyed or Modified . . . . . . . . . . . . . . . . . . . . . . . . . 151
6.4.1 Lifetime Dependencies Between Views and Their Ranges . . . . . . . . . . . . . . . 151
Contents vii

6.4.2 Views with Write Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152


6.4.3 Views on Ranges That Change . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
6.4.4 Copying Views Might Change Behavior . . . . . . . . . . . . . . . . . . . . . . . . . . 154
6.5 Views and const . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
6.5.1 Generic Code for Both Containers and Views . . . . . . . . . . . . . . . . . . . . . . . 156
6.5.2 Views May Remove the Propagation of const . . . . . . . . . . . . . . . . . . . . . . 163
6.5.3 Bringing Back Deep Constness to Views . . . . . . . . . . . . . . . . . . . . . . . . . 165
6.6 Summary of All Container Idioms Broken By Views . . . . . . . . . . . . . . . . . . . . . . . 168
6.7 Afternotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168

7 Utilities for Ranges and Views 171


7.1 Key Utilities for Using Ranges as Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
7.1.1 std::views::all() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
7.1.2 std::views::counted() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
7.1.3 std::views::common() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
7.2 New Iterator Categories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
7.3 New Iterator and Sentinel Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
7.3.1 std::counted_iterator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
7.3.2 std::common_iterator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
7.3.3 std::default_sentinel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
7.3.4 std::unreachable_sentinel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
7.3.5 std::move_sentinel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
7.4 New Functions for Dealing with Ranges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
7.4.1 Functions for Dealing with the Elements of Ranges (and Arrays) . . . . . . . . . . . 183
7.4.2 Functions for Dealing with Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
7.4.3 Functions for Swapping and Moving Elements/Values . . . . . . . . . . . . . . . . . 186
7.4.4 Functions for Comparisons of Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
7.5 New Type Functions/Utilities for Dealing with Ranges . . . . . . . . . . . . . . . . . . . . . . 189
7.5.1 Generic Types of Ranges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
7.5.2 Generic Types of Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
7.5.3 New Functional Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
7.5.4 Other New Types for Dealing with Iterators . . . . . . . . . . . . . . . . . . . . . . . . 191
7.6 Range Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
7.6.1 Benefits and Restrictions for Range Algorithms . . . . . . . . . . . . . . . . . . . . . 192
7.6.2 Algorithm Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
viii Contents

8 View Types in Detail 199


8.1 Overview of All Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
8.1.1 Overview of Wrapping and Generating Views . . . . . . . . . . . . . . . . . . . . . . 199
8.1.2 Overview of Adapting Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
8.2 Base Class and Namespace of Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
8.2.1 Base Class for Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
8.2.2 Why Range Adaptors/Factories Have Their Own Namespace . . . . . . . . . . . . . 203
8.3 Source Views to External Elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
8.3.1 Subrange . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
8.3.2 Ref View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
8.3.3 Owning View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
8.3.4 Common View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
8.4 Generating Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
8.4.1 Iota View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
8.4.2 Single View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
8.4.3 Empty View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
8.4.4 IStream View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
8.4.5 String View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
8.4.6 Span . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
8.5 Filtering Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
8.5.1 Take View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
8.5.2 Take-While View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
8.5.3 Drop View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
8.5.4 Drop-While View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
8.5.5 Filter View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
8.6 Transforming Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
8.6.1 Transform View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
8.6.2 Elements View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
8.6.3 Keys and Values View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
8.7 Mutating Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
8.7.1 Reverse View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
8.8 Views for Multiple Ranges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
8.8.1 Split and Lazy-Split View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
8.8.2 Join View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
Contents ix

9 Spans 295
9.1 Using Spans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
9.1.1 Fixed and Dynamic Extent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
9.1.2 Example Using a Span with a Dynamic Extent . . . . . . . . . . . . . . . . . . . . . . 296
9.1.3 Example Using a Span with Non-const Elements . . . . . . . . . . . . . . . . . . . . 301
9.1.4 Example Using a Span with Fixed Extent . . . . . . . . . . . . . . . . . . . . . . . . . 303
9.1.5 Fixed vs. Dynamic Extent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
9.2 Spans Considered Harmful . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
9.3 Design Aspects of Spans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
9.3.1 Lifetime Dependencies of Spans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
9.3.2 Performance of Spans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
9.3.3 const Correctness of Spans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
9.3.4 Using Spans as Parameters in Generic Code . . . . . . . . . . . . . . . . . . . . . . . 310
9.4 Span Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312
9.4.1 Span Operations and Member Types Overview . . . . . . . . . . . . . . . . . . . . . . 312
9.4.2 Constructors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
9.4.3 Operations for Sub-Spans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
9.5 Afternotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318

10 Formatted Output 319


10.1 Formatted Output by Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
10.1.1 Using std::format() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
10.1.2 Using std::format_to_n() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
10.1.3 Using std::format_to() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
10.1.4 Using std::formatted_size() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
10.2 Performance of the Formatting Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
10.2.1 Using std::vformat() and vformat_to() . . . . . . . . . . . . . . . . . . . . . . 323
10.3 Formatted Output in Detail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324
10.3.1 General Format of Format Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324
10.3.2 Standard Format Specifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324
10.3.3 Width, Precision, and Fill Characters . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
10.3.4 Format/Type Specifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
10.4 Internationalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329
10.5 Error Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
x Contents

10.6 User-Defined Formatted Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333


10.6.1 Basic Formatter API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
10.6.2 Improved Parsing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335
10.6.3 Using Standard Formatters for User-Defined Formatters . . . . . . . . . . . . . . . . 337
10.6.4 Using Standard Formatters for Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
10.7 Afternotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342

11 Dates and Timezones for <chrono> 343


11.1 Overview by Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
11.1.1 Scheduling a Meeting on the 5th of Every Month . . . . . . . . . . . . . . . . . . . . 343
11.1.2 Scheduling a Meeting on the Last Day of Every Month . . . . . . . . . . . . . . . . . 348
11.1.3 Scheduling a Meeting Every First Monday . . . . . . . . . . . . . . . . . . . . . . . . 350
11.1.4 Using Different Timezones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
11.2 Basic Chrono Concepts and Terminology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357
11.3 Basic Chrono Extensions with C++20 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358
11.3.1 Duration Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358
11.3.2 Clocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359
11.3.3 Timepoint Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360
11.3.4 Calendrical Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361
11.3.5 Time Type hh_mm_ss . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
11.3.6 Hours Utilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366
11.4 I/O with Chrono Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367
11.4.1 Default Output Formats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367
11.4.2 Formatted Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
11.4.3 Locale-Dependent Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
11.4.4 Formatted Input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372
11.5 Using the Chrono Extensions in Practice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
11.5.1 Invalid Dates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
11.5.2 Dealing with months and years . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 380
11.5.3 Parsing Timepoints and Durations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382
11.6 Timezones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
11.6.1 Characteristics of Timezones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
11.6.2 The IANA Timezone Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
11.6.3 Using Timezones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
11.6.4 Dealing with Timezone Abbreviations . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
Contents xi

11.6.5 Custom Timezones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391


11.7 Clocks in Detail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394
11.7.1 Clocks with a Specified Epoch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394
11.7.2 The Pseudo Clock local_t . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395
11.7.3 Dealing with Leap Seconds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
11.7.4 Conversions between Clocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397
11.7.5 Dealing with the File Clock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399
11.8 Other New Chrono Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401
11.9 Afternotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401

12 std::jthread and Stop Tokens 403


12.1 Motivation for std::jthread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
12.1.1 The Problem of std::thread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
12.1.2 Using std::jthread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405
12.1.3 Stop Tokens and Stop Callbacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 406
12.1.4 Stop Tokens and Condition Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408
12.2 Stop Sources and Stop Tokens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410
12.2.1 Stop Sources and Stop Tokens in Detail . . . . . . . . . . . . . . . . . . . . . . . . . . 411
12.2.2 Using Stop Callbacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 412
12.2.3 Constraints and Guarantees of Stop Tokens . . . . . . . . . . . . . . . . . . . . . . . . 417
12.3 std::jthread in Detail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418
12.3.1 Using Stop Tokens with std::jthread . . . . . . . . . . . . . . . . . . . . . . . . . . 419
12.4 Afternotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421

13 Concurrency Features 423


13.1 Thread Synchronization with Latches and Barriers . . . . . . . . . . . . . . . . . . . . . . . . . 423
13.1.1 Latches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 423
13.1.2 Barriers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427
13.2 Semaphores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431
13.2.1 Example of Using Counting Semaphores . . . . . . . . . . . . . . . . . . . . . . . . . 432
13.2.2 Example of Using Binary Semaphores . . . . . . . . . . . . . . . . . . . . . . . . . . . 436
13.3 Extensions for Atomic Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 439
13.3.1 Atomic References with std::atomic_ref<> . . . . . . . . . . . . . . . . . . . . . 439
13.3.2 Atomic Shared Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443
13.3.3 Atomic Floating-Point Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447
xii Contents

13.3.4 Thread Synchronization with Atomic Types . . . . . . . . . . . . . . . . . . . . . . . 447


13.3.5 Extensions for std::atomic_flag . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451
13.4 Synchronized Output Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 452
13.4.1 Motivation for Synchronized Output Streams . . . . . . . . . . . . . . . . . . . . . . . 452
13.4.2 Using Synchronized Output Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . 453
13.4.3 Using Synchronized Output Streams for Files . . . . . . . . . . . . . . . . . . . . . . 454
13.4.4 Using Synchronized Output Streams as Output Streams . . . . . . . . . . . . . . . . 455
13.4.5 Synchronized Output Streams in Practice . . . . . . . . . . . . . . . . . . . . . . . . . 456
13.5 Afternotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457

14 Coroutines 459
14.1 What Are Coroutines? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459
14.2 A First Coroutine Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461
14.2.1 Defining the Coroutine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461
14.2.2 Using the Coroutine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462
14.2.3 Lifetime Issues with Call-by-Reference . . . . . . . . . . . . . . . . . . . . . . . . . . 466
14.2.4 Coroutines Calling Coroutines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 468
14.2.5 Implementing the Coroutine Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . 471
14.2.6 Bootstrapping Interface, Handle, and Promise . . . . . . . . . . . . . . . . . . . . . . 477
14.2.7 Memory Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479
14.3 Coroutines That Yield or Return Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 481
14.3.1 Using co_yield . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 481
14.3.2 Using co_return . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 487
14.4 Coroutine Awaitables and Awaiters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491
14.4.1 Awaiters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491
14.4.2 Standard Awaiters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493
14.4.3 Resuming Sub-Coroutines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494
14.4.4 Passing Values From Suspension Back to the Coroutine . . . . . . . . . . . . . . . . 498
14.5 Afternotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503

15 Coroutines in Detail 505


15.1 Coroutine Constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505
15.1.1 Coroutine Lambdas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505
15.2 The Coroutine Frame and the Promises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507
15.2.1 How Coroutine Interfaces, Promises, and Awaitables Interact . . . . . . . . . . . . . 508
Contents xiii

15.3 Coroutine Promises in Detail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 515


15.3.1 Mandatory Promise Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 515
15.3.2 Promise Operations to Return or Yield Values . . . . . . . . . . . . . . . . . . . . . . 518
15.3.3 Optional Promise Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 520
15.4 Coroutine Handles in Detail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 521
15.4.1 std::coroutine_handle<void> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 522
15.5 Exceptions in Coroutines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 523
15.6 Allocating Memory for the Coroutine Frame . . . . . . . . . . . . . . . . . . . . . . . . . . . . 525
15.6.1 How Coroutines Allocate Memory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 525
15.6.2 Avoiding Heap Memory Allocation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 526
15.6.3 get_return_object_on_allocation_failure() . . . . . . . . . . . . . . . . . 530
15.7 co_await and Awaiters in Detail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 531
15.7.1 Details of the Awaiter Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 531
15.7.2 Letting co_await Update Running Coroutines . . . . . . . . . . . . . . . . . . . . . 532
15.7.3 Symmetric Transfer with Awaiters for Continuation . . . . . . . . . . . . . . . . . . . 536
15.8 Other Ways of Dealing with co_await . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 538
15.8.1 await_transform() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 538
15.8.2 operator co_await() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 541
15.9 Concurrent Use of Coroutines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 542
15.9.1 co_await Coroutines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 542
15.9.2 A Thread Pool for Coroutine Tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 546
15.9.3 What C++ Libraries Will Provide After C++20 . . . . . . . . . . . . . . . . . . . . . 555
15.10 Coroutine Traits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556

16 Modules 559
16.1 Motivation for Modules Using a First Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 559
16.1.1 Implementing and Exporting a Module . . . . . . . . . . . . . . . . . . . . . . . . . . 559
16.1.2 Compiling Module Units . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 561
16.1.3 Importing and Using a Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 562
16.1.4 Reachable versus Visible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 563
16.1.5 Modules and Namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 564
16.2 Modules with Multiple Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 566
16.2.1 Module Units . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 566
16.2.2 Using Implementation Units . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 566
16.2.3 Internal Partitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 571
xiv Contents

16.2.4 Interface Partitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 572


16.2.5 Summary of Splitting Modules into Different Files . . . . . . . . . . . . . . . . . . . 575
16.3 Dealing with Modules in Practice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 576
16.3.1 Dealing with Module Files with Different Compilers . . . . . . . . . . . . . . . . . . 576
16.3.2 Dealing with Header Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 578
16.4 Modules in Detail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 580
16.4.1 Private Module Fragments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 580
16.4.2 Module Declaration and Export in Detail . . . . . . . . . . . . . . . . . . . . . . . . . 582
16.4.3 Umbrella Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 583
16.4.4 Module Import in Detail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 584
16.4.5 Reachable versus Visible Symbols in Detail . . . . . . . . . . . . . . . . . . . . . . . 584
16.5 Afternotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 587

17 Lambda Extensions 589


17.1 Generic Lambdas with Template Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . 589
17.1.1 Using Template Parameters for Generic Lambdas in Practice . . . . . . . . . . . . . 590
17.1.2 Explicit Specification of Lambda Template Parameters . . . . . . . . . . . . . . . . . 591
17.2 Calling the Default Constructor of Lambdas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 592
17.3 Lambdas as Non-Type Template Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . 594
17.4 consteval Lambdas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 595
17.5 Changes for Capturing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596
17.5.1 Capturing this and *this . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596
17.5.2 Capturing Structured Bindings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 597
17.5.3 Capturing Parameter Packs of Variadic Templates . . . . . . . . . . . . . . . . . . . . 597
17.5.4 Lambdas as Coroutines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 599
17.6 Afternotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 600

18 Compile-Time Computing 601


18.1 Keyword constinit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 601
18.1.1 Using constinit in Practice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 603
18.1.2 How constinit Solves the Static Initialization Order Fiasco . . . . . . . . . . . . . 604
18.2 Keyword consteval . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 606
18.2.1 A First consteval Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 607
18.2.2 constexpr versus consteval . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 609
18.2.3 Using consteval in Practice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 612
Contents xv

18.2.4 Compile-Time Value versus Compile-Time Context . . . . . . . . . . . . . . . . . . . 613


18.3 Relaxed Constraints for constexpr Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . 614
18.4 std::is_constant_evaluated() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 614
18.4.1 std::is_constant_evaluated() in Detail . . . . . . . . . . . . . . . . . . . . . . 616
18.5 Using Heap Memory, Vectors, and Strings at Compile Time . . . . . . . . . . . . . . . . . . . 620
18.5.1 Using Vectors at Compile Time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 620
18.5.2 Returning a Collection at Compile Time . . . . . . . . . . . . . . . . . . . . . . . . . . 622
18.5.3 Using Strings at Compile Time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625
18.6 Other constexpr Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 628
18.6.1 constexpr Language Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 628
18.6.2 constexpr Library Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 628
18.7 Afternotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 629

19 Non-Type Template Parameter (NTTP) Extensions 631


19.1 New Types for Non-Type Template Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . 631
19.1.1 Floating-Point Values as Non-Type Template Parameters . . . . . . . . . . . . . . . . 632
19.1.2 Objects as Non-Type Template Parameters . . . . . . . . . . . . . . . . . . . . . . . . 634
19.1.3 Lambdas as Non-Type Template Parameters . . . . . . . . . . . . . . . . . . . . . . . 638
19.2 Afternotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 640

20 New Type Traits 641


20.1 New Type Traits for Type Classification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 642
20.1.1 is_bounded_array_v<> and is_unbounded_array_v . . . . . . . . . . . . . . . 642
20.2 New Type Traits for Type Inspection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 642
20.2.1 is_nothrow_convertible_v<> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 642
20.3 New Type Traits for Type Conversion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 643
20.3.1 remove_cvref_t<> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 643
20.3.2 unwrap_reference<> and unwrap_ref_decay_t . . . . . . . . . . . . . . . . . . 643
20.3.3 common_reference<>_t . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 643
20.3.4 type_identity_t<> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 644
20.4 New Type Traits for Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 645
20.4.1 iter_difference_t<> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 645
20.4.2 iter_value_t<> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 645
20.4.3 iter_reference_t<> and iter_rvalue_reference_t<> . . . . . . . . . . . . . 646
xvi Contents

20.5 Type Traits and Functions for Layout Compatibility . . . . . . . . . . . . . . . . . . . . . . . . 647


20.5.1 is_layout_compatible_v<> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 647
20.5.2 is_pointer_interconvertible_base_of_v<> . . . . . . . . . . . . . . . . . . . 648
20.5.3 is_corresponding_member() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 648
20.5.4 is_pointer_interconvertible_with_class() . . . . . . . . . . . . . . . . . . 649
20.6 Afternotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 649

21 Small Improvements for the Core Language 651


21.1 Range-Based for Loop with Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 651
21.2 using for Enumeration Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 652
21.3 Delegating Enumeration Types to Different Scopes . . . . . . . . . . . . . . . . . . . . . . . . 654
21.4 New Character Type char8_t . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 655
21.4.1 Changes in the C++ Standard Library for char8_t . . . . . . . . . . . . . . . . . . . 657
21.4.2 Broken Backward Compatibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 657
21.5 Improvements for Aggregates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 660
21.5.1 Designated Initializers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 660
21.5.2 Aggregate Initialization with Parentheses . . . . . . . . . . . . . . . . . . . . . . . . . 661
21.5.3 Definition of Aggregates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 665
21.6 New Attributes and Attribute Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 667
21.6.1 Attributes [[likely]] and [[unlikely]] . . . . . . . . . . . . . . . . . . . . . . . 667
21.6.2 Attribute [[no_unique_address]] . . . . . . . . . . . . . . . . . . . . . . . . . . . . 668
21.6.3 Attribute [[nodiscard]] with Parameter . . . . . . . . . . . . . . . . . . . . . . . . 670
21.7 Feature Test Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 671
21.8 Afternotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672

22 Small Improvements for Generic Programming 673


22.1 Implicit typename for Type Members of Template Parameters . . . . . . . . . . . . . . . . . 673
22.1.1 Rules for Implicit typename in Detail . . . . . . . . . . . . . . . . . . . . . . . . . . . 674
22.2 Improvements for Aggregates in Generic Code . . . . . . . . . . . . . . . . . . . . . . . . . . . 676
22.2.1 Class Template Argument Deduction (CTAD) for Aggregates . . . . . . . . . . . . . 676
22.3 Conditional explicit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 678
22.3.1 Conditional explicit in the Standard Library . . . . . . . . . . . . . . . . . . . . . . 680
22.4 Afternotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 681
Contents xvii

23 Small Improvements for the C++ Standard Library 683


23.1 Updates for String Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 683
23.1.1 String Members starts_with() and ends_with() . . . . . . . . . . . . . . . . . . 684
23.1.2 Restricted String Member reserve() . . . . . . . . . . . . . . . . . . . . . . . . . . . 684
23.2 std::source_location . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 685
23.3 Safe Comparisons of Integral Values and Sizes . . . . . . . . . . . . . . . . . . . . . . . . . . . 687
23.3.1 Safe Comparisons of Integral Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . 687
23.3.2 ssize() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 688
23.4 Mathematical Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 688
23.5 Utilities for Dealing with Bits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 689
23.5.1 Bit Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 690
23.5.2 std::bit_cast<>() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 692
23.5.3 std::endian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 692
23.6 <version> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 693
23.7 Extensions for Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 694
23.7.1 Range Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 694
23.7.2 New Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 694
23.7.3 unseq Execution Policy for Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . 696
23.8 Afternotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 698

24 Deprecated and Removed Features 701


24.1 Deprecated and Removed Core Language Features . . . . . . . . . . . . . . . . . . . . . . . . 701
24.2 Deprecated and Removed Library Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701
24.2.1 Deprecated Library Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701
24.2.2 Removed Library Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701
24.3 Afternotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 702

Glossary 703

Index 709
Preface

C++20 is the next evolution in modern C++ programming, and it is already (partially) supported by the
latest version of GCC, Clang, and Visual C++. The move to C++20 is at least as big a step as the move to
C++11. C++20 contains a significant number of new language features and libraries that will again change
the way we program in C++. This applies to both application programmers and programmers who provide
foundation libraries.

An Experiment
This book is an experiment in two ways:
• I am writing an in-depth book that covers complex new features invented and provided by different
programmers and C++ working groups. However, I can ask questions and I do.
• I am publishing the book myself on Leanpub and for printing on demand. That is, this book is written
step by step and I will publish new versions as soon there is a significant improvement that makes the
publication of a new version worthwhile.
The good thing is:
• You get the view of the language features from an experienced application programmer—somebody who
feels the pain a feature might cause and asks the relevant questions to be able to explain the motivation
for a feature, its design, and all consequences for using it in practice.
• You can benefit from my experience with C++20 while I am still learning and writing.
• This book and all readers can benefit from your early feedback.
This means that you are also part of the experiment. So help me out: give feedback about flaws, errors,
features that are not explained well, or gaps, so that we all can benefit from these improvements.

Acknowledgments
This book would not have been possible without the help and support of a huge number of people.
First of all, I would like to thank you, the C++ community, for making this book possible. The incredible
design of all the features of C++20, the helpful feedback, and your curiosity are the basis for the evolution
of a successful language. In particular, thanks for all the issues you told me about and explained and for the
feedback you gave.
xix
xx Preface

I would especially like to thank everyone who reviewed drafts of this book or corresponding slides and
provided valuable feedback and clarification. These reviews increased the quality of the book significantly,
again proving that good things are usually the result of collaboration between many people. Therefore, so
far (this list is still growing) huge thanks to Carlos Buchart, Javier Estrada, Howard Hinnant, Yung-Hsiang
Huang, Daniel Krügler, Dietmar Kühl, Jens Maurer, Paul Ranson, Thomas Symalla, Steve Vinoski, Ville
Voutilainen. Andreas Weis, Hui Xie, Leor Zolman, and Victor Zverovich.
In addition, I would like to thank everyone on the C++ standards committee. In addition to all the work
involved in adding new language and library features, these experts spent many, many hours explaining and
discussing their work with me, and they did so with patience and enthusiasm. Special thanks here go to
Howard Hinnant, Tom Honermann, Tomasz Kaminski, Peter Sommerlad, Tim Song, Barry Revzin, Ville
Voutilainen, and Jonathan Wakely.
Special thanks go to the LaTeX community for a great text system and to Frank Mittelbach for solving
my LATEX issues (it was almost always my fault).
And finally, many thanks go to my proofreader, Tracey Duffy, who has again done a tremendous job of
converting my “German English” into native English.

Versions of This Book


Because this book is written incrementally, the following is a history of the major updates (newest first):
• 2022-10-29: Describe missing details about floating-point and struct values as NTTPs.
• 2022-10-29: Describe missing hours utilities.
• 2022-10-28: Layout all pages.
• 2022-10-15: Describe missing details of the join view.
• 2022-10-06: Describe missing issues with standard views and list them all together.
• 2022-09-28: Describe more range/iterator utility details (move sentinels, iter_move(), iter_swap()).
• 2022-09-22: Update span documentation and document span operations in detail.
• 2022-09-16: Fix the utilities for layout compatibility.
• 2022-08-22: Add complete example programs and fix view details for each view.
• 2022-08-21: Describe details of the istream view that were added to C++20 as a late fix.
• 2022-08-17: Describe the new result type for range algorithms.
• 2022-08-05: Describe how to deal with a non-existing timezone database.
• 2022-07-29: Improve the description of const issues with views.
• 2022-07-27: Describe the effect of caching views in detail.
• 2022-07-21: Describe new iterator categories and iterator_concept.
• 2022-07-06: Describe concepts for non-type template parameters.
• 2022-06-28: Describe using requires for member functions to change APIs.
• 2022-06-19: Describe the fix to the formatting library for compile-time parsing of format strings.
• 2022-05-31: Split coroutine description into two chapters.
• 2022-05-24: Describe properties and details of all views.
• 2022-05-23: Describe coroutine exception handling.
• 2022-05-24: Describe nested coroutines and symmetric transfer.
• 2022-05-19: Describe asynchronous communication and thread pools for coroutines.
Versions of This Book xxi

• 2022-05-07: Describe jthread and stop token support for condition variables.
• 2022-04-28: Describe owning views, which were added to C++20 as a late fix.
• 2022-04-24: Describe significant details of coroutines and their awaiters.
• 2022-04-21: Describe functions for safe integral comparisons.
• 2022-04-21: Describe std::ranges::swap().
• 2022-04-20: Describe the new shift algorithms and the new min/max algorithms for ranges.
• 2022-04-20: Describe compare_three_way and lexicographical_compare_three_way().
• 2022-04-19: Describe feature test macros.
• 2022-04-18: Describe several additional details and clarifications about modules.
• 2022-04-09: Describe deferred evaluation with auto parameters.
• 2022-03-26: Describe awaiters, memory management, and other details of coroutines.
• 2022-03-08: Describe details of utilities of the ranges library.
• 2022-03-03: Rewrite the coroutines chapter with several more details.
• 2022-02-20: Describe the range-based for loop with initialization.
• 2022-02-17: Reorganize book chapters.
• 2022-02-16: Describe new attributes and attribute extensions.
• 2022-02-15: Describe conditional explicit.
• 2022-02-15: Fix the way user-defined formatters should be implemented.
• 2022-02-12: Describe using for enumeration values.
• 2022-02-09: Add figure with subset of the subsumptions of the standard concepts.
• 2022-01-08: Describe class template argument deduction for aggregates.
• 2022-01-05: Describe modified definition of aggregates.
• 2022-01-04: Describe aggregate initialization with parentheses.
• 2021-12-31: Describe the new iterator traits (such as std::iter_value_t).
• 2021-12-31: Describe where typename is no longer required for type members of template parameters.
• 2021-12-30: Describe new bit operations (including bit_cast<>()).
• 2021-12-29: Describe improvements for string types (including std::u8string and using strings at compile time).
• 2021-12-28: Describe the unseq execution policy for algorithms.
• 2021-12-25: Describe all other missing lambda features.
• 2021-12-11: Describe consteval lambdas.
• 2021-12-06: Describe std::endian.
• 2021-12-06: Describe synchronized output streams.
• 2021-12-04: Describe the header file <version>.
• 2021-11-21: Describe compile-time use of vectors and constexpr extensions.
• 2021-10-25: Describe designated initializers.
• 2021-10-14: Describe severe compatibility issues with operator==.
• 2021-10-14: Describe mathematical constants.
• 2021-10-12: Describe constinit, consteval, and std::is_constant_evaluated().
• 2021-10-07: Describe char8_t for UTF-8 (and its compatibility issues).
• 2021-10-02: Clarify which const views you cannot iterate over.
• 2021-10-01: Provide details about the formatting library.
• 2021-09-21: Add updates and fixes according to several reviews.
• 2021-09-20: Provide a first description of features for modules.
xxii Preface

• 2021-09-11: Discuss const issues of ranges and views.


• 2021-08-30: All concepts are documented.
• 2021-08-28: Document all new type traits.
• 2021-08-27: Document iterator utility functions for ranges.
• 2021-08-26: Document lazy_split_view<>, which was added to C++20 as a late fix.
• 2021-08-25: Document all missing views of C++20 and all_t<>.
• 2021-08-20: Document all new iterator and sentinel types.
• 2021-08-20: Document all primary range adaptors (counted(), all(), and common()).
• 2021-08-19: Document common_view.
• 2021-08-16: Document how to deal with semantic constraints of concepts.
• 2021-08-15: Document type_identity.
• 2021-07-31: Document empty_view, ref_view, and view_interface.
• 2021-07-29: Generic code for ranges has to use universal/forwarding references.
• 2021-07-28: Document iota_view and unreached_sentinel.
• 2021-07-27: Extend and reorganize chapters about ranges and views.
• 2021-07-17: Document subrange.
• 2021-07-09: Document std::source_location.
• 2021-06-29: The initial published version of the book. The following features are more or less completely described
or provide at least a full conceptual introduction:
– Comparisons and the spaceship operator <=>
– Generic functions
– Concepts and requirements (details open)
– Ranges and views (details open)
– Spans
– Non-type template parameter extensions
– Formatted output (overview only)
– Dates and timezones for <chrono>
– Coroutines (first examples)
– std::thread and stop tokens
– New concurrency features
About This Book

This book presents all the new language and library features of C++20. It covers the motivation and context
of each new feature with examples and background information.
As usual for my books, the focus lies on the application of the new features in practice and the book
demonstrates how features impact day-to-day programming and how you can benefit from them in projects.
This applies to both application programmers and programmers who provide generic frameworks and foun-
dation libraries.

What You Should Know Before Reading This Book


To get the most from this book, you should already be familiar with C++. You should be familiar with the
concepts of classes and references in general, and you should be able to write C++ code using components
of the C++ standard library, such as IOStreams and containers. You should also be familiar with the basic
features of Modern C++, such as auto or the range-based for loop and other basic features introduced with
C++11, C++14, and C++17.
However, you do not have to be an expert. My goal is to make the content understandable for the average
C++ programmer who does not necessarily know all the details or all of the latest features. I discuss basic
features and review more subtle issues as the need arises.
This ensures that the text is accessible to experts and intermediate programmers alike.

Overall Structure of the Book


This book covers all changes to C++ introduced with C++20. This applies to both language and library
features as well as both features that affect day-to-day application programming and features for the sophis-
ticated implementation of (foundation) libraries. However, the more general cases and examples usually
come first.
The different chapters are grouped, but the grouping has no deeper didactic reasoning other than that it
makes sense to first introduce features that might be used by the subsequent features introduced afterwards.
In principle, you can read the chapters in any order. If features from different chapters are combined, there
are corresponding cross-references.

xxiii
xxiv About This Book

How to Read This Book


Do not be intimidated by the number of pages in this book. As always with C++, things can become pretty
complicated when you look into details. For a basic understanding of the new features, the introductory
motivations and examples are often sufficient.
In my experience, the best way to learn something new is to look at examples. Therefore, you will find
a lot of examples throughout the book. Some are just a few lines of code that illustrate an abstract concept,
whereas others are complete programs that provide a concrete application of the material. The latter kind of
examples are introduced by a C++ comment that describes the file that contains the program code. You can
find these files on the website for this book at http://www.cppstd20.com.

The Way I Implement


Note the following hints about the way I write code and comments in this book.

Initializations
I usually use the modern form of initialization (introduced in C++11 as uniform initialization) with curly
braces or with = in trivial cases:
int i = 42;
std::string s{"hello"};
The brace initialization has the following advantages:
• It can be used with fundamental types, class types, aggregates, enumeration types, and auto
• It can be used to initialize containers with multiple values
• It can detect narrowing errors (e.g., initialization of an int by a floating-point value)
• It cannot be confused with function declarations or calls
If the braces are empty, the default constructors of (sub)objects are called and fundamental data types are
guaranteed to be initialized with 0/false/nullptr.

Error Terminology
I often talk about programming errors. If there is no special hint, the term error or a comment such as
... // ERROR
means a compile-time error. The corresponding code should not compile (with a conforming compiler).
If I use the term runtime error, the program might compile but not behave correctly or result in undefined
behavior (thus, it might or might not do what is expected).
The C++ Standards xxv

Code Simplifications
I try to explain all features with helpful examples. However, to concentrate on the key aspects to be taught,
I might often skip other details that should be part of the code.
• Most of the time I use an ellipsis (“...”) to signal additional code that is missing. Note that I do not use
code font here. If you see an ellipsis with code font, code must have these three dots as a language feature
(such as for “typename...”).
• In header files, I usually skip the preprocessor guards. All header files should have something like the
following:
#ifndef MYFILE_HPP
#define MYFILE_HPP
...
#endif // MYFILE_HPP
Therefore, please beware and fix the code when using these header files in your projects.

The C++ Standards


C++ has different versions defined by different C++ standards.
The original C++ standard was published in 1998 and was subsequently amended by a technical corri-
gendum in 2003, which provided minor corrections and clarifications to the original standard. This “old C++
standard” is known as C++98 or C++03.
The world of “Modern C++” began with C++11 and was extended with C++14 and C++17. The interna-
tional C++ standards committee now aims to issue a new standard every three years. Clearly, that leaves less
time for massive additions, but it brings the changes to the broader programming community more quickly.
The development of larger features, therefore, takes time and might cover multiple standards.
C++20 is now the beginning of the next “Even more Modern C++” evolution. Again, several ways of
programming will probably change. However, as usual, compilers need some time to provide the latest
language features. At the time of writing this book, C++20 is already at least partially supported by major
compilers. However, as usual, compilers differ greatly in their support of new different language features.
Some will compile most or even all of the code in this book, while others may only be able to handle a
significant subset. I expect that this problem will soon be resolved as programmers everywhere demand
standard support from their vendors.

Example Code and Additional Information


You can access all example programs and find more information about this book from its website, which has
the following URL:
http://www.cppstd20.com
xxvi About This Book

Feedback
I welcome your constructive input—both negative and positive. I have worked very hard to bring you what
I hope you will find to be an excellent book. However, at some point I had to stop writing, reviewing, and
tweaking to “release the new revision.” You may therefore find errors, inconsistencies, presentations that
could be improved, or topics that are missing altogether. Your feedback gives me a chance to fix these issues,
inform all readers about the changes through the book’s website, and improve any subsequent revisions or
editions.
The best way to reach me is by email. You will find the email address on the website for this book:
http://www.cppstd20.com

If you use the ebook, you might want to ensure you to have the latest version of this book available (remem-
ber it is written and published incrementally). You should also check the book’s website for the currently
known errata before submitting reports. Please always refer to the publishing date of this version when
giving feedback. The current publishing date is 2022-10-30 (you can also find it on page ii, the page directly
after the cover).
Many thanks.
Chapter 1
Comparisons and Operator <=>

C++20 simplifies the definition of comparisons for user-defined types and introduces better ways of dealing
with them. The new operator <=> (also called the spaceship operator) was introduced for this purpose.
This chapter explains how to define and deal with comparisons since C++20 using these new features.

1.1 Motivation for Operator<=>


Let us look first at the motivation for the new way comparisons are handled since C++20 and the new
operator <=>.

1.1.1 Defining Comparison Operators Before C++20


Before C++20, you had to define six operators for a type to provide full support for all possible comparisons
of its objects.
For example, if you wanted to compare objects of a type Value (having an integral ID), you had to
implement the following:
class Value {
private:
long id;
...
public:
...
// equality operators:
bool operator== (const Value& rhs) const {
return id == rhs.id; // basic check for equality
}
bool operator!= (const Value& rhs) const {
return !(*this == rhs); // derived check
}

1
2 Chapter 1: Comparisons and Operator <=>

// relational operators:
bool operator< (const Value& rhs) const {
return id < rhs.id; // basic check for ordering
}
bool operator<= (const Value& rhs) const {
return !(rhs < *this); // derived check
}
bool operator> (const Value& rhs) const {
return rhs < *this; // derived check
}
bool operator>= (const Value& rhs) const {
return !(*this < rhs); // derived check
}
};
This enables you to call any of the six comparison operators for a Value (the object the operator is defined
for) with another Value (passed as parameter rhs). For example:
Value v1, v2;
... ;
if (v1 <= v2) { // calls v1.operator<=(v2)
...
}
The operators might also be called indirectly (e.g., by calling sort()):
std::vector<Value> coll;
... ;
std::sort(coll.begin(), coll.end()); // uses operator < to sort
Since C++20 , the call might alternatively use ranges:
std::ranges::sort(coll); // uses operator < to sort
The problem is that even though most of the operators are defined in terms of other operators (they are all
based on either operator == or operator <), the definitions are tedious and they add a lot of visual clutter.
In addition, for a well-implemented type, you might need more:
• Declare the operators with noexcept if they cannot throw
• Declare the operators with constexpr if they can be used at compile time
• Declare the operators as “hidden friends” (declare them with friend inside the class structure so that both
operands become parameters and support implicit type conversions) if the constructors are not explicit
• Declare the operators with [[nodiscard]] to force warnings if the return value is not used
1.1 Motivation for Operator<=> 3

For example:
lang/valueold.hpp
 
class Value {
private:
long id;
...
public:
constexpr Value(long i) noexcept // supports implicit type conversion
: id{i} {
}
...
// equality operators:
[[nodiscard]] friend constexpr
bool operator== (const Value& lhs, const Value& rhs) noexcept {
return lhs.id == rhs.id; // basic check for equality
}
[[nodiscard]] friend constexpr
bool operator!= (const Value& lhs, const Value& rhs) noexcept {
return !(lhs == rhs); // derived check for inequality
}

// relational operators:
[[nodiscard]] friend constexpr
bool operator< (const Value& lhs, const Value& rhs) noexcept {
return lhs.id < rhs.id; // basic check for ordering
}
[[nodiscard]] friend constexpr
bool operator<= (const Value& lhs, const Value& rhs) noexcept {
return !(rhs < lhs); // derived check
}
[[nodiscard]] friend constexpr
bool operator> (const Value& lhs, const Value& rhs) noexcept {
return rhs < lhs; // derived check
}
[[nodiscard]] friend constexpr
bool operator>= (const Value& lhs, const Value& rhs) noexcept {
return !(lhs < rhs); // derived check
}
};
 
4 Chapter 1: Comparisons and Operator <=>

1.1.2 Defining Comparison Operators Since C++20


Since C++20, a couple of things have changed regarding comparison operators.

Operator == Implies Operator !=


To check for inequality, it is now enough to define operator ==.
When the compiler finds no matching declaration for an expression a!=b, the compiler rewrites the
expressions and looks for !(a==b) instead. If that does not work, the compiler also tries to change the order
of the operands, so it also tries !(b==a):
a != b // tries: a!=b, !(a==b), and !(b==a)
Therefore, for a of TypeA and b of TypeB, the compiler will be able to compile
a != b
It can do this if there is
• A free-standing operator!=(TypeA, TypeB)
• A free-standing operator==(TypeA, TypeB)
• A free-standing operator==(TypeB, TypeA)
• A member function TypeA::operator!=(TypeB)
• A member function TypeA::operator==(TypeB)
• A member function TypeB::operator==(TypeA)
Directly calling a defined operator != is preferred (but the order of the types has to fit). Changing the order
of the operands has the lowest priority. Having both a free-standing and a member function is an ambiguity
error.
Thus, with
bool operator==(const TypeA&, const TypeB&);
or
class TypeA {
public:
...
bool operator==(const TypeB&) const;
};
the compiler will be able to compile:
MyType a;
MyType b;
...
a == b; // OK: fits perfectly
b == a; // OK, rewritten as: a == b
a != b; // OK, rewritten as: !(a == b)
b != a; // OK, rewritten as: !(a == b)
Note that thanks to rewriting, implicit type conversions for the first operand are also possible when rewriting
converts the operand so that it becomes the parameter of the defined member function.
See the example sentinel1.cpp for how to benefit from this feature by only defining a member operator
== when != with a different order of operands is called.
1.1 Motivation for Operator<=> 5

Operator <=>
There is no equivalent rule that for all relational operators it is enough to have operator < defined. However,
you only have to define the new operator <=>.
In fact, the following is enough to enable programmers to use all possible comparators:
lang/value20.hpp
 
#include <compare>

class Value {
private:
long id;
...
public:
constexpr Value(long i) noexcept
: id{i} {
}
...
// enable use of all equality and relational operators:
auto operator<=> (const Value& rhs) const = default;
};
 
In general, operator == handles the equality of objects by defining == and !=, while operator <=> handles
the order of objects by defining the relational operators. However, by declaring an operator<=> with
=default, we use a special rule that a defaulted member operator<=>:
class Value {
...
auto operator<=> (const Value& rhs) const = default;
};
generates a corresponding member operator==, so that we effectively get:
class Value {
...
auto operator<=> (const Value& rhs) const = default;
auto operator== (const Value& rhs) const = default; // implicitly generated
};
The effect is that both operators use their default implementation, which compares objects member by
member. This means that the order of the members in the class matters.
Thus, with
class Value {
...
auto operator<=> (const Value& rhs) const = default;
};
we get all we need to be able to use all six comparison operators.
6 Chapter 1: Comparisons and Operator <=>

In addition, even when declaring the operator as a member function, the following applies for the gener-
ated operators:
• They are noexcept if comparing the members never throws
• They are constexpr if comparing the members is possible at compile time
• Thanks to rewriting, implicit type conversions for the first operand are also supported
This reflects that, in general, operator== and operator<=> handle different but related things:
• operator== defines equality and can be used by the equality operators == and !=.
• operator<=> defines the ordering and can be used by the relational operators <, <=, >, and >=.
Note that you have to include header <compare> when defaulting or using operator <=>.
#include <compare>
However, most header files for standard types (strings, containers, <utility>) include this header anyway.

Implementing Operator <=>


To have more control over the generated comparison operators, you can define operator== and operator<=>
yourself. For example:
lang/value20def.hpp
 
#include <compare>

class Value {
private:
long id;
...
public:
constexpr Value(long i) noexcept
: id{i} {
}
...
// for equality operators:
bool operator== (const Value& rhs) const {
return id == rhs.id; // defines equality (== and !=)
}
// for relational operators:
auto operator<=> (const Value& rhs) const {
return id <=> rhs.id; // defines ordering (<, <=, >, and >=)
}
};
 
This means that you can specify which members in which order matter or implement special behavior.
The way these basic operators work is that if an expression uses one of the comparison operators and
does not find a matching direct definition, the expression is rewritten so that it can use these operators.
1.2 Defining and Using Comparisons 7

Corresponding to rewriting calls of equality operators, rewriting might also change the order of relational
operands, which might enable implicit type conversion for the first operand. For example, if
x <= y
does not find a matching definition of operator<=, it might be rewritten as
(x <=> y) <= 0
or even
0 <= (y <=> x)
As you can see by this rewriting, the new operator<=> performs a three-way comparison, which yields a
value you can compare with 0:
• If the value of x<=>y is equal to 0, x and y are equal or equivalent.
• If the value of x<=>y is less than 0, x is less than y.
• If the value of x<=>y is greater than 0, x is greater than y.
However, note that the return type of operator<=> is not an integral value. The return type is a type that
signals the comparison category, which could be strong ordering, weak ordering, or partial ordering. These
types support the comparison with 0 to deal with the result.

1.2 Defining and Using Comparisons


The following sections explain the details of the handling of the comparison operators since C++20.

1.2.1 Using Operator<=>


Operator <=> is a new binary operator. It is defined for all fundamental data types for which the relational
operators are defined. As usual, it can be user-defined as operator<=>().
Operator <=> takes precedence over all other comparison operators, which means that you need paren-
theses to use it in an output statement but not to compare its result with another value:
std::cout << (0 < x <=> y) << '\n'; // calls 0 < (x <=> y)
Please note that you have to include a specific header file to deal with the result of operator <=>:
#include <compare>
This applies to declaring it (as defaulted), implementing it, or using it. For example:
#include <compare> // for calling <=>

auto x = 3 <=> 4; // does not compile without header <compare>


Most header files for standard types (strings, containers, <utility>) include this header anyway. However,
to call the operator on values or types that do not require this header, you have to include <compare>.
Note that operator <=> is for implementing types. Outside the implementation of an operator<=>,
programmers should never invoke <=> directly. Although you can, you should never write a<=>b < 0
instead of a<b.
8 Chapter 1: Comparisons and Operator <=>

1.2.2 Comparison Category Types


The new operator <=> does not return a Boolean value. Instead, it acts similarly to three-way-comparisons
yielding a negative value to signal less, a positive value to signal greater, and 0 to signal equal or equivalent.
This behavior is similar to the return value of the C function strcmp(); however, there is an important
difference: the return value is not an integral value. Instead, the C++ standard library provides three possible
return types, which reflect the category of the comparison.

Comparison Categories
When comparing two values to put them in an order, we have different categories of behavior that could
happen:
• With strong ordering (also called total ordering), any value of a given type is less than or equal to or
greater than any other value of this type (including itself).
Typical examples of this category are integral values or common string types. A string s1 is less than
or equal to or greater than a string s2.
If a value of this category is neither less than nor greater than another value, both values are equal. If
you have multiple objects, you can sort them in ascending or descending order (with equal values having
any order among each other).
• With weak ordering, any value of a given type is less than or equivalent to or greater than any other
value of this type (including itself). However, equivalent values do not have to be equal (have the same
value).
A typical example of this category is a type for case-insensitive strings. A string "hello" is less than
"hello1" and greater than "hell". However, "hello" is equivalent to "HELLO" although these two
strings are not equal.
If a value of this category is neither less than nor greater than another value, both values are at least
equivalent (they might even be equal). If you have multiple objects, you can sort them in ascending or
descending order (with equivalent values having any order among each other).
• With partial ordering, any value of a given type could be less than or equivalent to or greater than any
other value of this type (including itself). However, in addition, it may not be possible to specify a specific
order between two values at all.
A typical example of this category are floating-point types, because they might have the special value
NaN (“not a number”). Any comparison with NaN yields false. Therefore, in this case a comparison
might yield that two values are unordered and the comparison operator might return one of four values.
If you have multiple objects, you might not be able to sort them in ascending or descending order
(unless you ensure that values that cannot be ordered are not there).

Comparison Category Types in the Standard Library


For the different comparison categories, the C++20 standard introduces the following types:
• std::strong_ordering with the values:
– std::strong_ordering::less
– std::strong_ordering::equal
(also available as std::strong_ordering::equivalent)
– std::strong_ordering::greater
1.2 Defining and Using Comparisons 9

• std::weak_ordering with the values:


– std::weak_ordering::less
– std::weak_ordering::equivalent
– std::weak_ordering::greater
• std::partial_ordering with the values:
– std::partial_ordering::less
– std::partial_ordering::equivalent
– std::partial_ordering::greater
– std::partial_ordering::unordered
Note that all types have the values less, greater, and equivalent. However, strong_ordering also
has equal, which is the same as equivalent there, and partial_ordering has the value unordered,
representing neither less nor equal nor greater.
Stronger comparison types have implicit type conversions to weaker comparison types. This means that
you can use any strong_ordering value as weak_ordering or partial_ordering value (equal then
becomes equivalent).

1.2.3 Using Comparison Categories with operator<=>


The new operator <=> should return a value of one of the comparison category types, representing the result
of the comparison combined with the information about whether this result is able to create a strong/total,
weak, or partial ordering.
For example, this is how a free-standing operator<=> might be defined for a type MyType:
std::strong_ordering operator<=> (MyType x, MyOtherType y)
{
if (xIsEqualToY) return std::strong_ordering::equal;
if (xIsLessThanY) return std::strong_ordering::less;
return std::strong_ordering::greater;
}
Or, as a more concrete example, defining operator<=> for a type MyType:
class MyType {
...
std::strong_ordering operator<=> (const MyType& rhs) const {
return value == rhs.value ? std::strong_ordering::equal :
value < rhs.value ? std::strong_ordering::less :
std::strong_ordering::greater;
}
};

However, it is usually easier to define the operator by mapping it to results of underlying types. Therefore,
it would be better for the member operator<=> above to just yield the value and category of its member
value:
10 Chapter 1: Comparisons and Operator <=>

class MyType {
...
auto operator<=> (const MyType& rhs) const {
return value <=> rhs.value;
}
};
This not only returns the right value; it also ensures that the return value has the right comparison category
type depending on the type of the member value.

1.2.4 Calling Operator <=> Directly


You can call any defined operator <=> directly:
MyType x, y;
...
x <=> y // yields a value of the resulting comparison category type
As written, you should only call operator <=> directly when implementing operator<=>. However, it can
be very helpful to know the returned comparison category.
As also written, operator<=> is predefined for all fundamental types for which the relational operators
are defined. For example:
int x = 17, y = 42;
x <=> y // yields std::strong_ordering::less
x <=> 17.0 // yields std::partial_ordering::equivalent
&x <=> &x // yields std::strong_ordering::equal
&x <=> nullptr // ERROR: relational comparison with nullptr not supported
In addition, all types of the C++ standard library that provide relational operators also provide operator<=>
now. For example:
std::string{"hi"} <=> "hi" // yields std::strong_ordering::equal;
std::pair{42, 0.0} <=> std::pair{42, 7.7} // yields std::partial_ordering::less
For your own type(s), you only have to define operator<=> as a member or free-standing function.
Because the return type depends on the comparison category, you can check against a specific return
value:
if (x <=> y == std::partial_ordering::equivalent) // always OK
Due to the implicit type conversions to weaker ordering types, this will even compile if operator<=> yields
a strong_ordering or weak_ordering value.
The other way around does not work. If the comparison yields a weak_ordering or partial_ordering
value, you cannot compare it with a strong_ordering value.
if (x <=> y == std::strong_ordering::equal) // might not compile
However, a comparison with 0 is always possible and usually easier:
if (x <=> y == 0) // always OK
In addition, operator<=> might be called indirectly due to the new rewriting of relational operator calls:
if (!(x < y || y < x)) // might call operator<=> to check for equality
1.2 Defining and Using Comparisons 11

Or:
if (x <= y && y <= x) // might call operator<=> to check for equality
Note that operator!= is never rewritten to call operator<=>. However, it might call an operator==
member that is implicitly generated due to a defaulted operator<=> member.

1.2.5 Dealing with Multiple Ordering Criteria


To compute the result of operator<=> based on multiple attributes, you can usually implement just a chain
of sub-comparisons until the result is not equal/equivalent or you reach the final attribute to be compared:
class Person {
...
auto operator<=> (const Person& rhs) const {
auto cmp1 = lastname <=> rhs.lastname; // primary member for ordering
if (cmp1 != 0) return cmp1; // return result if not equal
auto cmp2 = firstname <=> rhs.firstname; // secondary member for ordering
if (cmp2 != 0) return cmp2; // return result if not equal
return value <=> rhs.value; // final member for ordering
}
};
However, the return type does not compile if the attributes have different comparison categories. For exam-
ple, if a member name is a string and a member value is a double, we have conflicting return types:
class Person {
std::string name;
double value;
...
auto operator<=> (const Person& rhs) const { // ERROR: different return types deduced
auto cmp1 = name <=> rhs.name;
if (cmp1 != 0) return cmp1; // return strong_ordering for std::string
return value <=> rhs.value; // return partial_ordering for double
}
};
In that case, you can use a conversion to the weakest comparison type. If you know the weakest comparison
type, you can just declare it as the return type:
class Person {
std::string name;
double value;
...
std::partial_ordering operator<=> (const Person& rhs) const { // OK
auto cmp1 = name <=> rhs.name;
if (cmp1 != 0) return cmp1; // strong_ordering converted to return type
return value <=> rhs.value; // partial_ordering used as the return type
}
};
12 Chapter 1: Comparisons and Operator <=>

If you do not know the comparison types (e.g., their type is a template parameter), you can use a new type
trait std::common_comparison_category<> that computes the strongest comparison category:
class Person {
std::string name;
double value;
...
auto operator<=> (const Person& rhs) const // OK
-> std::common_comparison_category_t<decltype(name <=> rhs.name),
decltype(value <=> rhs.value)> {
auto cmp1 = name <=> rhs.name;
if (cmp1 != 0) return cmp1; // used as or converted to common comparison type
return value <=> rhs.value; // used as or converted to common comparison type
}
};
By using the trailing return type syntax (with auto in front and the return type after ->), we can use the
parameters to compute the comparison types. Even though in this case, you could just use name instead of
rhs.name, this approach works in general (e.g., also for free-standing functions).
If you want to provide a stronger category than the one that is used internally, you have to map all possible
values of the internal comparsions to values of the return type. This might include some error handling if
you cannot map some values. For example:
class Person {
std::string name;
double value;
...
std::strong_ordering operator<=> (const Person& rhs) const {
auto cmp1 = name <=> rhs.name;
if (cmp1 != 0) return cmp1; // return strong_ordering for std::string
auto cmp2 = value <=> rhs.value; // might be partial_ordering for double
// map partial_ordering to strong_ordering:
assert(cmp2 != std::partial_ordering::unordered); // RUNTIME ERROR if unordered
return cmp2 == 0 ? std::strong_ordering::equal
: cmp2 > 0 ? std::strong_ordering::greater
: std::strong_ordering::less;
}
};
The C++ standard library provides some helper function objects for this. For example, to map floating-point
values, you can call std::strong_order() for the two values to be compared:
class Person {
std::string name;
double value;
...
std::strong_ordering operator<=> (const Person& rhs) const {
auto cmp1 = name <=> rhs.name;
1.3 Defining operator<=> and operator== 13

if (cmp1 != 0) return cmp1; // return strong_ordering for std::string


// map floating-point comparison result to strong ordering:
return std::strong_order(value, rhs.value);
}
};
If possible, std::strong_order() yields a std::strong_ordering value according to the passed argu-
ments as follows:
• Using strong_order(val1, val2) for the passed types if defined
• Otherwise, if the passed values are floating-point types, using the value of totalOrder() as specified in
ISO/IEC/IEEE 60559 (for which, e.g., -0 is less than +0 and -NaN is less than any non-NAN value and
+NaN)
• Using the new function object std::compare_three_way{}(val1, val2) if defined for the passed
types
This is the easiest way to give floating-point types a strong ordering which even works at runtime if one or
both of the operands might have the value NaN.
std::compare_three_way is a new function object type for calling operator <=>, just like std::less
is a function object type for calling operator <.
For other types that have a weaker ordering and operators == and < defined, you can use the function
object std::compare_strong_order_fallback() accordingly:
class Person {
std::string name;
SomeType value;
...
std::strong_ordering operator<=> (const Person& rhs) const {
auto cmp1 = name <=> rhs.name;
if (cmp1 != 0) return cmp1; // return strong_ordering for std::string
// map weak/partial comparison result to strong ordering:
return std::compare_strong_order_fallback(value, rhs.value);
}
};
Table Function objects for mapping comparison category types lists all available helper functions for map-
ping comparison category types.
To define operator<=> for a generic type, you should also consider using the function object
std::compare_three_way or the algorithm std::lexicographical_compare_three_way().

1.3 Defining operator<=> and operator==


Both operator<=> and operator== can be defined for your data types:
• Either as a member function taking one parameter
• Or as a free-standing function taking two parameters
14 Chapter 1: Comparisons and Operator <=>

Function Object in std:: Effect


strong_order() Maps to a strong order value also for floating-point
values
weak_order() Maps to a weak order value also for floating-point
values
partial_order() Maps to a partial order value
compare_strong_order_fallback() Maps to a strong order value even if only == and <
are defined
compare_weak_order_fallback() Maps to a weak order value even if only == and < are
defined
compare_partial_order_fallback() Maps to a partial order value even if only == and <
are defined

Table 1.1. Function objects for mapping comparison category types

1.3.1 Defaulted operator== and operator<=>


Inside a class or data structure (as a member or friend function), all comparison operators can be declared
as defaulted with =default. However, this usually only makes sense for operator== and operator<=>.
The member functions have to take the second parameter as const lvalue reference (const &). Friend
functions might alternatively take both parameters by value.
The defaulted operators require the support of the members and possible base classes:
• Defaulted operators == require the support of == in the members and base classes.
• Defaulted operators <=> require the support of == and either an implemented operator < or a defaulted
operator <=> in the members and base classes (for details, see below).
For the generated defaulted operators, the following then applies:
• The operator is noexcept if comparing the members guarantees not to throw.
• The operator is constexpr if comparing the members is possible at compile time.
For empty classes, the defaulted operators compare all objects as equal: operators ==, <=, and >= yield true,
operators !=, <, and > yield false, and <=> yields std::strong_ordering::equal.

1.3.2 Defaulted operator<=> Implies Defaulted operator==


If and only if an operator<=> member is defined as defaulted, then by definition a corresponding operator==
member is also defined if no defaulted operator== is provided. All aspects (visibility, virtual, attributes,
requirements, etc.) are adopted. For example:
template<typename T>
class Type {
...
public:
[[nodiscard]] virtual std::strong_ordering
operator<=>(const Type&) const requires(!std::same_as<T,bool>) = default;
};
1.3 Defining operator<=> and operator== 15

is equivalent to the following:


template<typename T>
class Type {
...
public:
[[nodiscard]] virtual std::strong_ordering
operator<=> (const Type&) const requires(!std::same_as<T,bool>) = default;

[[nodiscard]] virtual bool


operator== (const Type&) const requires(!std::same_as<T,bool>) = default;
};
For example, the following is enough to support all six comparison operators for objects of the type Coord:
lang/coord.hpp
 
#include <compare>

struct Coord {
double x{};
double y{};
double z{};
auto operator<=>(const Coord&) const = default;
};
 
Note again that the member function must be const and that the parameter must be declared to be a const
lvalue reference (const &).
You can use this data structure as follows:
lang/coord.cpp
 
#include "coord.hpp"
#include <iostream>
#include <algorithm>

int main()
{
std::vector<Coord> coll{ {0, 5, 5}, {5, 0, 0}, {3, 5, 5},
{3, 0, 0}, {3, 5, 7} };

std::sort(coll.begin(), coll.end());
for (const auto& elem : coll) {
std::cout << elem.x << '/' << elem.y << '/' << elem.z << '\n';
}
}
 
16 Chapter 1: Comparisons and Operator <=>

The program has the following output:


0/5/5
3/0/0
3/5/5
3/5/7
5/0/0

1.3.3 Implementation of the Defaulted operator<=>


If operator<=> is defaulted and you have members or base classes and you call one of the relational
operators, then the following happens:
• If operator<=> is defined for a member or base class, that operator is called.
• Otherwise, operator== and operator< are called to decide whether (from the point of view of the
members or base classes)
– The objects are equal/equivalent (operator== yields true)
– The objects are less or greater
– The objects are unordered (only when partial ordering is checked)
In that case, the return type of the defaulted operator<=> calling these operators cannot be auto.
For example, consider the following declarations:
struct B {
bool operator==(const B&) const;
bool operator<(const B&) const;
};

struct D : public B {
std::strong_ordering operator<=> (const D&) const = default;
};
Then:
D d1, d2;
d1 > d2; // calls B::operator== and possibly B::operator<
If operator== yields true, we know that the result of > is false and that is it. Otherwise, operator< is
called to find out whether the expression is true or false.
With
struct D : public B {
std::partial_ordering operator<=> (const D&) const = default;
};
the compiler might even call operator< twice to find out whether there is any order at all.
With
struct B {
bool operator==(const B&) const;
bool operator<(const B&) const;
};
1.4 Overload Resolution with Rewritten Expressions 17

struct D : public B {
auto operator<=> (const D&) const = default;
};
the compiler does not compile any call with relational operators because it cannot decide which ordering
category the base class has. In that case, you need operator<=> in the base class too.
However, checks for equality work, because in D, operator== is automatically declared equivalent to
the following:
struct D : public B {
auto operator<=> (const D&) const = default;
bool operator== (const D&) const = default;
};
This means that we have the following behavior:
D d1, d2;
d1 > d2; // ERROR: cannot deduce comparison category of operator<=>
d1 != d2; // OK (note: only tries operator<=> and B::operator== of a base class)
Equality checks always use only operator== of a base class (which might be generated according to a
defaulted operator<=> though). Any operator< or operator!= in the base class is ignored.
The same applies, if D has a member of type B.

1.4 Overload Resolution with Rewritten Expressions


Let us finally elaborate on the evaluation of expressions with comparison operators with the support of
rewritten calls.

Calling Equality Operators


To compile
x != y
the compiler might now try all of the following:
x.operator!=(y) // calling member operator!= for x
operator!=(x, y) // calling a free-standing operator!= for x and y

!x.operator==(y) // calling member operator== for x


!operator==(x, y) // calling a free-standing operator== for x and y

!x.operator==(y) // calling member operator== generated by operator<=> for x

!y.operator==(x) // calling member operator== generated by operator<=> for y


The last form is tried to support an implicit type conversion for the first operand, which requires that the
operand is a parameter.
18 Chapter 1: Comparisons and Operator <=>

In general, the compiler tries to call:


• A free-standing operator !=: operator!=(x, y)
or a member operator !=: x.operator!=(y)
Having both operators != defined is an ambiguity error.
• A free-standing operator ==: !operator==(x, y)
or a member operator ==: !x.operator==(y)
Note that the member operator == may be generated from a defaulted operator<=> member.
Again, having both operators == defined is an ambiguity error. This also applies if the member
operator== is generated due to a defaulted operator<=>.
When an implicit type conversion for the first operand v is necessary, the compiler also tries to reorder the
operands. Consider:
42 != y // 42 implicitly converts to the type of y
In that case, the compiler tries to call in that order:
• A free-standing or member operator !=
• A free-standing or member operator == (note that the member operator == may be generated from a
defaulted operator<=> member)1
Note that a rewritten expression never tries to call a member operator !=.

Calling Relational Operators


For the relational operators we have similar behavior, except that the rewritten statements fall back on the
new operator <=> and compare the result with 0. The operator behaves like a three-way comparison function
returning a negative value for less, 0 for equal, and a positive value for greater (the returned value is not a
numeric value; it is only a value that supports the corresponding comparisons).
For example, to compile
x <= y
the compiler might now try all of the following:
x.operator<=(y) // calling member operator<= for x
operator<=(x, y) // calling a free-standing operator<= for x and y

x.operator<=>(y) <= 0 // calling member operator<=> for x


operator<=>(x, y) <= 0 // calling a free-standing operator<=> for x and y

0 <= y.operator<=>(x) // calling member operator<=> for y


Again, the last form is tried to support an implicit type conversion for the first operand, for which it has to
become a parameter.

1 The original C++20 standard was fixed here slightly with http://wg21.link/p2468r2.
1.5 Using Operator <=> in Generic Code 19

1.5 Using Operator <=> in Generic Code


In generic code, the new operator <=> provides some challenges. This is because there might be types that
provide operator <=> and there might be types that provide some or all of the basic comparison operators.

1.5.1 compare_three_way
std::compare_three_way is a new function object type for calling operator <=>, just like std::less is
a function object type for calling operator <.
You can use it as follows:
• To compare values of a generic type
• As a default type when you have to specify the type of a function object
For example:
template<typename T>
struct Value {
T val{};
...
auto operator<=> (const Value& v) const noexcept(noexcept(val<=>val)) {
return std::compare_three_way{}(val<=>v.val);
}
};
Using std::compare_three_way has (like std::less) the benefit that it even defines a total order for
raw pointers (which is not the case for operators <=> or <). Therefore, you should use it when generic types
are used that can be raw pointer types.
To allow programmers to forward declare operator<=>(), C++20 also introduces the type trait
std::compare_three_way_result with the alias template std::compare_three_way_result_t:
template<typename T>
struct Value {
T val{};
...
std::compare_three_way_result_t<T,T>
operator<=> (const Value& v) const noexcept(noexcept(val<=>val));
};

1.5.2 Algorithm lexicographical_compare_three_way()


To be able to compare two ranges and yield a value of the matching comparison category, C++20 also in-
troduced the algorithm lexicographical_compare_three_way(). This algorithm is particularly helpful
for implementing operator<=> for members that are collections.
Another Random Scribd Document
with Unrelated Content
her whom they sought.
She sat in the path before it, still with the fowling-piece across her
knees. But to reach her they had to pass the body of a soldier lying
with clenched hands in a crimson patch of snow. The child, who had
passed by many horrors on the road, and all with gay unconcern,
stretched out his arms across this one, recognising his mother at
once, and kicking in his father's clasp.
She raised her eyes dully. She was too weak even to move. "I knew
you would come," she said in a whisper; and with that her eyes
shifted and settled on the body in the path.
"Take him away! I—I did not kill him."
Her husband set down the child. "Run indoors, little one: you shall
kiss mamma presently."
He bent over her, and, unstringing a small wine-skin from his belt,
held the mouth of it to her lips. The priest stooped over the dead
man, on whose collar the figures "28" twinkled in the sunlight. The
child, for a moment rebellious, toddled towards the doorway of the
hut.
Mercedes' eyelids had closed: but some of the wine found its way
down her throat, and as it revived her, they flickered again.
"Sebastian," she whispered.
"Be at rest, dear wife. It is I, Sebastian."
"I did not kill him."
"I hear. You did not kill him."
"The child?"
"He is safe—safe and sound," he assured her, and called,
"Sebastianillo!"
For a moment there was no answer: but as he lifted Mercedes and
carried her into the hut, on its threshold the boy met them, his both
hands dropping silver dollars.
THE LAMP AND THE GUITAR
[FROM THE MEMOIRS OF MANUEL, OR MANUS, MacNEILL, AN
AGENT IN THE SECRET SERVICE OF GREAT BRITAIN DURING
THE PENINSULAR CAMPAIGNS OF 1808-13.]

I have not the precise date in 1811 when Fuentes and I set out for
Salamanca, but it must have been either in the third or fourth week
of July.
In Portugal just then Lord Wellington was fencing, so to speak, with
the points of three French armies at once. On the south he had
Soult, on the north Dorsenne, and between them Marmont's troops
were scattered along the valley of the Tagus, with Madrid as their far
base. Being solidly concentrated, by short and rapid movements he
could keep these three armies impotent for offence; but en
revanche, he could make no overmastering attack upon any one of
them. If he advanced far against Soult or against Dorsenne he must
bring Marmont down on his flank, left or right; while, if he reached
out and struck for the Tagus Valley, Marmont could borrow from
right and left without absolutely crippling his colleagues, and roll up
seventy thousand men to bar the road on Madrid. In short, the
opposing armies stood at a deadlock, and there were rumours that
Napoleon, who was pouring troops into Spain from the north, meant
to follow and take the war into his own hands.
Now, the strength and the weakness of the whole position lay with
Marmont; while the key of it, curiously enough, was Ciudad Rodrigo,
garrisoned by Dorsenne—as in due time appeared. For the present,
Wellington, groping for the vital spot, was learning all that could be
learnt about Marmont's strength, its disposition, and (a matter of
first importance) its victualling, Spain being a country where large
armies starve. How many men were being drafted down from the
north? How was Marmont scattering his cantonments to feed them?
What was the state of the harvest? What provisions did Salamanca
contain? And what stores were accumulating at Madrid, Valladolid,
Burgos?
I had just arrived at Lisbon in a chassemarée of San Sebastian,
bringing a report of the French troops, which for a month past had
been pouring across the bridge of Irun: and how I had learnt this is
worth telling. There was a cobbler, Martinez by name—a little man
with a green shade over his eyes—who plied his trade in a wooden
hutch at the end of the famous bridge. While he worked he counted
every man, horse, standard, wagon, or gun that passed, and
forwarded the numbers without help of speech or writing (for he
could not even write his own name). He managed it all with his
hammer, tapping out a code known to our fellows who roamed the
shore below on the pretence of hunting for shellfish, but were
prevented by the French cordon from getting within sight of the
bridge. As for Martinez, the French Generals themselves gossipped
around his hutch while he cobbled industriously at the soldiers'
shoes.
I had presented my report to Lord Wellington, who happened to be
in Lisbon quarrelling with the Portuguese Government and re-
embarking (apparently for Cadiz) a battering train of guns and
mortars which had just arrived from England: and after two days'
holiday I was spending an idle morning in a wine-shop by the quay,
where the proprietor, a fervid politician, kept on file his copies of the
Government newspaper, the Lisbon Gazette. A week at sea had
sharpened my appetite for news; and I was wrapped in study of the
Gazette when an orderly arrived from headquarters with word that
Lord Wellington requested my attendance there at once.
I found him in conference with a handsome, slightly built man—a
Spaniard by his face—who stepped back as I entered, but without
offering to retire. Instead, he took up his stand with his back to one
of the three windows overlooking the street, and so continued to
observe me, all the while keeping his own face in shade.
The General, as his habit was, came to business at once.
"I have sent for you," said he, "on a serious affair. Our
correspondents in Salamanca have suddenly ceased to write."
"If your Excellency's correspondents are the same as the
Government's," said I, "'tis small wonder," and I glanced at the
newspaper in his hand—a copy of the same Gazette I had been
reading.
"Then you also think this is the explanation?" He held out the paper
with the face of a man handling vermin.
"The Government publishes its reports, the English newspapers copy
them: these in turn reach Paris; the Emperor reads them: and,"
concluded I, with a shrug, "your correspondents cease to write,
probably for the good reason that they are dead."
"That is just what I want you to find out," said he.
"Your Excellency wishes me to go to Salamanca? Very good. And,
supposing these correspondents to be dead?"
"You will find others."
"That may not be easy: nevertheless, I can try. Your Excellency, by
the way, will allow me to promise that future reports are not for
publication?"
Wellington smiled grimly, doubtless from recollection of a recent
interview with Silveira and the Portuguese Ministry. "You may rest
assured of that," said he; and added: "There may be some delay, as
you suggest, in finding fresh correspondents: and it is very
necessary for me to know quickly how Salamanca stands for stores."
"Then I must pick up some information on my own account."
"The service will be hazardous——"
"Oh, as for that——" I put in, with another shrug.
"—and I propose to give you a companion," pursued Wellington,
with a half-turn toward the man in the recess of the window. "This is
Señor Fuentes. You are not acquainted, I believe?—as you ought to
be."
Now from choice I have always worked alone: and had the General
uttered any other name I should have been minded to protest, with
the old Greek, that two were not enough for an army, while for any
other purpose they were too many. But on hearsay the
performances of this man Fuentes and his methods and his
character had for months possessed a singular fascination for me.
He was at once a strolling guitar-player and a licentiate of the
University of Salamanca, a consorter with gypsies, and by birth a
pure-blooded Castilian hidalgo. Some said that patriotism was a
passion with him; with a face made for the love of women, he had a
heart only for the woes of Spain. Others averred that hatred of the
French was always his master impulse; that they, by demolishing the
colleges of his University, and in particular his own beloved College
of San Lorenzo, had broken his heart and first driven him to wander.
Rewards he disdained; dangers he laughed at: his feats in the
service had sometimes a touch of high comedy and always a touch
of heroic grace. In short, I believe that if Spain had held a poet in
those days, Fuentes would have passed into song and lived as one
of his country's demigods.
He came forward now with a winning smile and saluted me cordially,
not omitting a handsome compliment on my work. You could see
that the man had not an ounce of meanness in his nature.
"We shall be friends," said he, turning to the Commander-in-Chief.
"And that will be to the credit of both, since Señor MacNeill has an
objection to comrades."
"I never said so."
"Excuse me, but I have studied your methods."
"Well, then," I replied, "I had the strongest objection, but you have
made me forget it—as you have forgotten your repugnance to visit
Salamanca." For although Fuentes flitted up and down and across
Spain like a will-o'-the-wisp, I had heard that he ever avoided the
city where he had lived and studied.
His fine eyes clouded, and he muttered some Latin words as it were
with a voice indrawn.
"I beg your pardon?" put in Wellington sharply.
"Cecidit, cecidit Salmantica illa fortis," Fuentes repeated.
"'Cecidit'—ah! I see—a quotation. Yes, they are knocking the place
about: as many as fifteen or sixteen colleges razed to the ground."
He opened the newspaper again and ran his eyes down the report.
"You'll excuse me: in England we have our own way of pronouncing
Latin, and for the moment I didn't quite catch——Yes, sixteen
colleges; a clean sweep! But before long, Señor Fuentes, we'll return
the compliment upon their fortifications."
"That must be my consolation, your Excellency," Fuentes made
answer with a smile which scarcely hid its irony.
The General began to discuss our route: our precautions he left to
us. He was well aware of the extreme risk we ran, and once again
made allusion to it as he dismissed us.
"If that were all your Excellency demanded!"
Fuentes' gaiety returned as we found ourselves in the street. "We
shall get on together like a pair of schoolboys," he assured me. "We
understand each other, you and I. But oh, those islanders!"

We left Lisbon that same evening on muleback, taking the road for
Abrantes. So universally were the French hated that the odds were
we might have dispensed with precautions at this stage, and indeed
for the greater part of the journey. The frontier once passed we
should be travelling in our native country—Fuentes as a gypsy and I
as an Asturian, moving from one harvest-job to another. We carried
no compromising papers: and if the French wanted to arrest folks on
mere suspicion they had the entire population to practise on.
Nevertheless, having ridden north-east for some leagues beyond
Abrantes—on the direct road leading past Ciudad Rodrigo to
Salamanca—we halted at Amendoa, bartered one of our mules for a
couple of skins of wine and ten days' provisions, and, having made
our new toilet in a chestnut grove outside the town, headed back for
the road leading east through Villa Velha into the Tagus valley.
Beyond the frontier we were among Marmont's cantonments: but
these lay scattered, and we avoided them easily. Keeping to the hill-
tracks on the northern bank of the river, and giving a wide berth to
the French posts in front of Alcantara, we struck away boldly for the
north through the Sierras: reached the Alagon, and, following up its
gorges, crossed the mountains in the rear of Bejar, where a French
force guarded the military pass.
So far we had travelled unmolested, if toilsomely; and a pleasanter
comrade than Fuentes no man could ask for. His gaiety never failed
him: yet it was ever gentle, and I suspected that it covered either a
native melancholy or some settled sorrow—sorrow for his country,
belike—but there were depths he never allowed me to sound. He did
everything well, from singing a love-song to tickling a trout and
cooking it for our supper: and it was after such a supper, as we lay
and smoked on a heathery slope beyond Bejar, that he unfolded his
further plans.
"My friend", said he, "there were once two brothers, students of
Salamanca, and not far removed in age. Of these the elder was
given to love-making and playing on the guitar; while the other
stuck to his books—which was all the more creditable because his
eyes were weak. I hope you are enjoying this story?"
"It begins to be interesting."
"Yet these two brothers—they were nearly of one height, by the way
—obtained their bachelor's degrees, and in time their licentiates,
though as rewards for different degrees of learning. They were from
Villacastin, beyond Avila in Old Castille: but their father, a hidalgo of
small estates there, possessed also a farm and the remains of a
castle across the frontier in the kingdom of Leon, a league to the
west of Salvatierra on the Tormes. It had come to him as security for
a loan which was never paid: and, dying, he left this property to his
younger son Andrea. Now when the French set a Corsican upon the
throne of our kingdoms, these two brothers withdrew from
Salamanca; but while Andrea took up his abode on his small
heritage, and gave security for his good behaviour, Eugenio, the
elder, turned his back on the paternal home (which the French had
ravaged), and became a rebel, a nameless, landless man and a
wanderer, with his guitar for company. You follow me?"
"I follow you, Señor Don Eugenio——"
"Not 'de Fuentes,'" he put in with a smile. "The real name you shall
read upon certain papers and parchments of which I hope to
possess myself to-night. In short, my friend, since we are on the
way to Salamanca, why should I not apply there for my doctor's
degree?"
"It requires a thesis, I have always understood."
"That is written."
"May I ask upon what subject?"
"The fiend take me if I know yet! But it is written, safe enough."
"Ah, I see! We go to Salvatierra? Yes, yes, but what of me, who
know scarcely any Latin beyond my credo?"
"Why, that is where I feel a certain delicacy. Having respect to your
rank, caballero, I do not like to propose that you should become my
servant."
"I am your servant already, and for a week past I have been an
Asturian. It will be promotion."
He sprang up gaily. "What a comrade is mine!" he cried, flinging
away the end of his cigarette. "To Salvatierra, then—Santiago, and
close Spain!"
Darkness overtook us as we climbed down the slopes: but we
pushed on, Fuentes leading the way boldly. Evidently he had come
to familiar ground. But it was midnight before he brought me, by an
abominable road, to a farmstead the walls of which showed
themselves ruinous even in the starlight—for moon there was none.
At an angle of the building, which once upon a time had been
whitewashed, rose a solid tower, with a doorway and an iron-
studded door, and a narrow window overlooking it. In spite of the
hour, Fuentes advanced nonchalantly and began to bang the door,
making noise enough to wake the dead. The window above was
presently opened—one could hear, with a shaking hand. "Who is
there?" asked a man's voice no less tremulous. "Who are you, for
the love of God?"
"Gente de paz, my dear brother!—not your friends the French. I
hope, by the way, you are entertaining none."
"I have been in bed these four hours or five. 'Peace,' say you? I wish
you would take your own risks and leave me in peace! What is it you
want, this time?"
"'Tis a good six weeks, brother, since my last visit: and, as you
know, I never call without need."
"Well, what is it you need?"
"I need," said Fuentes with great gravity, "the loan of your
spectacles."
"Be serious, for God's sake! And do not raise your voice so: the
French may be following you——"
"Dear Andrea, and if the French were to hear it, surely mine is an
innocent request. A pair of spectacles!"
"The French——" began Don Andrea and broke off, peering down
short-sightedly into the courtyard. "Ah, there is someone else! Who
is it? Who is it you have there in the darkness?"
"Dios! A moment since you were begging for silence, and now you
want me to call out my friend's name—to who knows what ears? He
has a mule, here, and I—oh yes, beside the spectacles I shall require
a horse: a horse, and—let me see—a treatise."
"Have you been drinking, brother?"
"No: and, since you mention it, a cup of wine, too, would not come
amiss. Is this a way to treat the caballero my friend? For the honour
of the family, brother, step down and open the door."
Don Andrea closed the window, and by-and-by we heard the bolts
withdrawn, one by one—and they were heavy. The door opened at
length, and a thin man in a nightcap peered out upon us with an oil-
lamp held aloft over the hand shading his eyes.
"You had best call Juan," said his brother easily, "and bid him stable
the mule. For the remainder of the night we are your guests; and, to
ensure our sleeping well, you shall fetch out the choicest of the
theses you have composed for your doctorate and read us a portion
over our wine."
We lay that night, after a repast of thin wine and chestnuts, in a
spare chamber, and on beds across the feet of which the rats
scudded. I did not see Don Andrea again: but his brother, who had
risen betimes, awakened me from uneasy slumber and showed me
his spoil. Sure enough it included a pair of spectacles and a bulky roll
of manuscript, a leathern jerkin, a white shirt, and a pair of velvet-
fustian breeches, tawny yellow in hue and something the worse for
wear. Below-stairs, in the courtyard, we found a white-haired
retainer waiting, with his grip on the bridles of my mule and a raw-
boned grey mare.
"The caballero will bring them back when he has done with them?"
said this old man as I mounted. The request puzzled me for a
moment until I met his eyes and found them fastened wistfully on
my breeches.
Assuredly Fuentes was an artist. Besides the spectacles, which in
themselves transformed him, he had borrowed a broad-brimmed hat
and a rusty black sleeveless mancha, which, by the way he contrived
it to hang, gave his frame an extraordinary lankiness. But his final
and really triumphant touch was simply a lengthening of the stirrups,
so that his legs dangled beneath the mare's belly like a couple of
ropes with shoes attached. If Don Andrea watched us out of sight
from his tower—as I doubt not he did—his emotions as he
recognised his portrait must have been lively.
In this guise we ambled steadily all day along the old Roman road
leading to Salamanca, and came within sight of the city as the sun
was sinking. It stood on the eastern bank of the river, fronting the
level rays, its walls rising tier upon tier, its towers and cupolas of
cream-coloured stone bathed in gold, with recesses of shadowy
purple. A bridge of twenty-five or six arches spanned the cool river-
beds, and towards this we descended between cornfields, of which
the light swept the topmost ears while the stalks stood already in
twilight. Truly it was a noble city yet, and so I cried aloud to
Fuentes. But his eyes, I believe, saw only what the French had
marred or demolished.
A group of their soldiery idled by the bridge-end, waiting for the
guard to be relieved, and lolled against the parapet watching the
bathers, whose shouts came up to me from the chasm below. But
instead of riding up and presenting our passes, Fuentes, a furlong
from the bridge, turned his mare's head to the left and reined up at
the door of a small riverside tavern.
The innkeeper—a brisk, athletic man, with the air of a retired
servant—appeared at the door as we dismounted. He scanned
Fuentes narrowly, while giving him affable welcome. Plainly he
recognised him as an old patron, yet plainly the recognition was
imperfect.
"Eh, my good Bartolomé, and so you still cling above the river? I
hope custom clings here too?"
"But—but can it be the Señor Don——"
"Eugenio, my friend. The spectacles puzzle you: they belong to my
brother, Don Andrea, and I may tell you that after a day's wear I find
them trying to the eyes. But, you understand, there are reasons ...
and so you will suppose me to be Don Andrea, while bringing a cup
of wine, and another for my servant, to Don Eugenio's favourite
seat, which was at the end of the garden beyond the mulberry-tree,
if you remember."
"Assuredly this poor house is your Lordship's, and all that belongs to
it. The wine shall be fetched with speed. But as for the table at the
end of the garden, I regret to tell your Lordship that it is occupied
for a while. If for this evening, I might recommend the parlour——"
The innkeeper made his excuse with a certain quick trepidation
which Fuentes did not fail to note.
"What is this? Your garden full? It appears then, my good Bartolomé,
that your custom has not suffered in these bad times."
"On the contrary, Señor, it has fallen off woefully! My garden has
been deserted for months, and is empty now, save for two
gentlemen, who, as luck will have it, have chosen to seat themselves
in your Lordship's favourite corner. Ah, yes, the old times were the
best! and I was a fool to grumble, as I sometimes did, when my
patrons ran me off my legs."
"But steady, Bartolomé: not so fast! Surely there used to be three
tables beyond the mulberry-tree, or my memory is sadly at fault."
"Three tables? Yes, it is true there are three tables. Nevertheless
——"
"I cannot see," pursued Fuentes with a musing air—"no, for the life
of me I cannot see how two gentlemen should require three tables
to drink their wine at."
"Nor I, Señor. It must, as you say, be a caprice: nevertheless they
charged me that on all accounts they were to have that part of the
garden to themselves."
"A very churlish caprice, then! They are Frenchmen, doubtless?"
"No, indeed, your Lordship: but two lads of good birth, gentlemen of
Spain, the one a bachelor, the other a student of the University."
"All the more, then, they deserve a lesson. Bartolomé, you will tell
your tapster to bring my wine to the vacant table beyond the
mulberry-tree."
"But, Señor——" As Fuentes moved off, the innkeeper put forth a
hand to entreat if not to restrain him.
"Eh?" Fuentes halted as if amazed at his impudence. "Ah, to be sure,
I am Don Andrea: but do not forget, my friend, that Don Eugenio
used to be quick-tempered, and that in members of one family these
little likenesses crop up in the most unexpected fashion." He strode
away down the shadowy garden-path over which in the tree-tops a
last beam or two of sunset lingered: and I, having hitched up our
beasts, followed him, carrying the saddle-bags and his guitar-case.
Three tables, as he had premised, stood in the patch of garden
beyond the mulberry-tree, hedged in closely on three sides, giving a
view in front upon the towers and fortifications across the river; a
nook secluded as a stage-box facing a scene that might have been
built and lit up for our delectation. The tables, with benches
alongside, stood moderately close together—two by the river-wall,
the third in the rear, where the hedge formed an angle: and the two
gentlemen so jealous of their privacy were seated at the nearer of
the two tables overlooking the river, and on the same bench—
though at the extreme ends of it and something more than a yard
apart.
They stared up angrily at our intrusion, and for the moment the
elder of the pair seemed about to demand our business. But Fuentes
walked calmly by, took his seat at the next table, pulled out his
bundle of manuscript, adjusted his spectacles, and began to read.
Having deposited my baggage, I took up a respectful position behind
him, ignoring—somewhat ostentatiously perhaps—the strangers'
presence, yet not without observing them from the corner of my
eye.
They were young: the elder, maybe, three-and-twenty, short, thick-
set, with features just now darkened by his ill-humour, but probably
sullen enough at the best of times: the younger, tall and nervous
and extraordinarily fair for a Spaniard, with a weak, restless mouth
and restless, passionate eyes. Indeed, either this restlessness was a
disease with him or he was suffering just now from an uncontrollable
agitation. Eyes, mouth, feet, fingers—the whole man seemed to be
twitching. I set down his age at eighteen. On the table stood a large
flask of wine, from which he helped himself fiercely, and beside the
flask lay a long bundle wrapped in a cloak.
This young man, having drained his glass at a gulp, let out an oath
and sprang up suddenly with a glare upon Fuentes, who had
stretched out his legs and was already absorbed in his reading.
"Señor Stranger," he began impetuously, "we would have you to
know, if the innkeeper has not already told you——"
"Gently!" interposed his comrade. "You are going the wrong way to
work. My friend, Sir"—he addressed Fuentes, who looked up with a
mild surprise—"my friend, Sir, was about to suggest that the light is
poor for reading."
"Oh," answered Fuentes, smiling easily, "for a minute or two—until
they bring my wine. Moreover, I wear excellent glasses."
"But the place is not too well chosen."
Fuentes appeared to digest this for a moment, then turned around
upon me with a puzzled air.
"My good Pedro, you have not misled me, I hope? I am short-
sighted, gentlemen; and if we have strayed into a private garden I
offer you my profoundest apologies." He gathered his manuscript
into a roll and stood up.
"To be plain with you, Sir," said the dark man sullenly, "this is not
precisely a private garden, and yet we desire privacy."
"Oho?" After a glance around, Fuentes fixed his eyes on the bundle
lying on the table. "And at the point of the sword—eh?"
The two young men started and at once began to eye each other
suspiciously.
"No, no," Fuentes assured them, smiling; "this is no trap, believe
me, but a chance encounter; and I am no alguacil in disguise, but a
poor scholar returning to Salamanca for his doctorate. Nor do I seek
to know the cause of your quarrel. But here comes the wine!" He
waited until the tapster had set flask and glasses on the table and
withdrawn. "In the interval before your friends arrive you will not
grudge me, Sirs, the draining of a glass to remembrance in a garden
where I too have loved my friends, and quarrelled with them, in
days gone by—days older now than I care to reckon." He raised the
wine and held it up for a moment against the sunset. "Youth—
youth!" he sighed.
"You are welcome, Sir," said the younger man a trifle more
graciously; "but we expect no seconds, and, believe me, we shall
presently be pressed for time."
Fuentes raised his eyebrows. "You surprise and shock me, Sirs. In
the days to which I drank just now it was not customary for
gentlemen of the University of Salamanca to fight without witnesses.
We left that to porters and grooms."
"And pray," sneered the darker young man, "may we know the name
of him who from the height of his years and experience presumes to
intrude this lecture on us?"
"You may address me, if you will, as Don Andrea Galazza de
Villacastin, a licentiate of your University——"
To my astonishment the younger man stopped him with a short
offensive laugh. "You may spare us the rest, Sir. Don Andrea Galazza
is known to us and to all honest patriots by repute: we can supply
the rest of his titles for ourselves, beginning with renegado——"
"Hist!" interposed his comrade, at the same time catching up the
swords from the table. "Don't be a fool, Sebastian—speak lower, for
God's sake!—the very soldiers at the bridge will hear you!"
"Ay, Sir," chimed in Fuentes gravely; "listen to your friend's advice,
and do not increase the peril of your remarks by the foolishness of
shouting them."
But the youngster, flushed with wine and overstrung, had lost for the
moment all self-control. "I accept that risk," cried he, "for the
pleasure of telling Don Andrea Galazza what kind of man he passes
for among honourable folk. He, the brother of Don Eugenio—of our
hero, the noble Fuentes! He, that signed his peace while that noble
heart preferred to break!" He spat in furious contempt.
Fuentes turned to me quietly. "Behold one of the enthusiasts we
came to seek," he murmured; "and one who will not fear risks. But
these testimonials are embarrassing, and this fame of mine swells to
a nuisance." He faced his accuser. "Nevertheless," answered he
aloud, "you make a noise that must disconcert your friend, who is in
two minds about assassinating me. Why spoil his game by arousing
the neighbourhood?"
"Señor Don Andrea, you know too much—thanks to my friend here,"
said the dark man slowly.
"But we are not assassins," put in the youngster. "Renegade though
you be, Don Andrea, I give you your chance." He snatched the foil
from his senior's hand and presented it solemnly, hilt foremost, to
Fuentes.
"Youth—youth!" murmured Fuentes with an appreciative laugh, as
he tucked the foil under his arm, took off his spectacles and rubbed
them, laughing again. He readjusted them carefully and, saluting,
fell on guard. "I am at your service, Sir."
The youth stepped forward hotly, touched blades, and almost
immediately lunged. An instant later his sword, as though it had
been a bird released from his hand, flew over his shoulder into the
twilight behind.
"That was ill-luck for you, Señor," said Fuentes lowering his point.
"But who can be sure of himself in this confounded twilight?" He
swung half-about towards the river-wall, with a glance across at the
city, where already a few lights began to twinkle in the dusk. And, so
turning, he seemed on a sudden to catch his breath.
And almost on that instant the youngster, who had fallen back
disconcerted, sprang forward in a fresh fury and gripped his
comrade by the arm, pointing excitedly towards a group of houses
above the fortifications, whence from a high upper storey, deeply
recessed between flanking walls, a light redder than the rest
twinkled across to us.
"The proof!" cried he. "She knew you would be here, and that is the
proof! You at least I will kill before I leave this garden, as I came to
kill you to-night."
In his new gust of fury he seemed to have forgotten his discomfiture
—to have forgotten even the existence of Fuentes, who now faced
them both with a smile which (unless the dusk distorted it) had
some bitterness in its raillery.
"If I mistake not, Sirs, the light you were discussing signals to us
from an upper chamber in the Lesser Street of the Virgins. It can
only be seen from this garden and from the far end of it, where we
now stand. I will not ask you who lights it now: but she who lit it in
former days was named Luisa. Oh yes, she was circumspect—a good
maid then, and no doubt a good maid now: in that street of the
Virgins there was at least one prudent. Youth flies, ay de mi! But
youth also, as I perceive to-night, repeats itself; and Luisa—who was
always circumspect, though a conspirator—apparently repeats
herself too."
"Luisa? What do you know of Luisa?" stammered the younger man.
The name seemed to have fallen on him like the touch of an
enchanter's wand, stiffening him to stone. Like a statue he stood
there, peering forward with a white face.
"My friend"—Fuentes turned to me—"be so good as to unstrap the
case yonder and hand me my guitar."
He laid his foil on the table, took the guitar from me, and, having
seated himself on the bench, tried the strings softly, all the while
looking up with grave raillery at the two young men.
"What do I know of Luisa? Listen!" Under his voice he began a light-
hearted little song, which in English might run like this, or as nearly
as I can contrive—
My love, she lives in Salamanca
All up a dozen flights of stairs;
There with the sparrows night and morning
Under the roof she chirps her prayers.
They say her wisdom comes from heaven—
So near the clouds and chimneys meet—
I rather think Luisa's sparrows
Fetch it aloft there from the street!

What would you have? In la Verdura


All the day long she keeps a stall:
Students, bachelors buy her nosegays,
Given with a look and—well, that's all!
Go, silly boy, believe you first with her—
Twenty at once she'll entertain.
Why love a mistress and be curst with her?
Copy Luisa—love all Spain!
He paused, still eyeing them. "You recognise the tune, Sirs? Does
she play it yet? Well, then, I made it for her."
"You? How came you to make her that tune?" The younger man had
found his voice at length. "No, Sir; coquette she may be, but that
she ever was friends with such a one as Andrea Galazza I will not
yet believe."
"And you are right. Sirs, you have not yet told me your names: but
in your generous heat you have given me your secret—that you are
two lovers of Spain, and even such a pair as my friend and I have
travelled some distance to seek. In return you shall have mine. I
tricked you just now. I am not Don Andrea, but his brother Eugenio
—or, as some call him, Fuentes."
"Fuentes! You!"
"Upon my honour, yes." He pulled off his spectacles, meeting their
incredulity with a frank laugh. "What proof can I give you?" The
guitar still lay across his knees: he picked it up as if to play, but set it
down after a moment with another laugh, hard and bitter. "Let us go
together, gentlemen, to the Street of the Virgins, and ask Luisa if she
remembers me."
It was agreed that the young men—who gave their names as Diego
de Ribalta and Sebastian Paz—should not accompany us into the
city, but wend their way back across the bridge, while we finished
our wine and mounted our beasts at leisure. The officer at the
bridge-end made no pother about our passports (borrowed, I need
scarcely say, from the estimable Don Andrea, who, as his brother
explained, was a careful man, and zealous in all dealings with the
authorities); and by-and-by we were clattering up-hill through the ill-
lighted streets of Salamanca. At the head of the first street our two
friends stepped out of the shadow and joined us in silence. In
silence, too, Fuentes regreeted them, and led the way—to an inn
first, the Four Crowns, standing almost under the shadow of the Old
Cathedral, where we stabled mare and mule; then, on foot, through
a maze of zigzagging lanes and alleys, back into the depths of a
waterside quarter. Once he was at fault—the lane we followed
ending abruptly in an open space strewn with rubble-heaps, a broad
area where the French had lately been at work. Among these heaps
he blundered for a while in the darkness, and then, retracing his
steps, took up the scent again and led us down one narrow street,
across another; turned to the right, counting the houses as he went,
and knocked at the twelfth door without hesitation. The knock was a
peculiar one—five quick taps, followed, after a pause, by one distinct
and heavy.
"But I must ask these gentlemen to do what remains," said he,
turning and addressing our companions. "Luisa has doubtless
changed the password since my time."
"Willingly, Señor Fuentes," agreed de Ribalta. "You will not, of
course, object to be blindfolded?—a formality, merely, in your case."
The porter, having received the password in a whisper through the
grille, unbolted to us, and opened the door upon a pitch-dark
passage. Here we submitted to have our eyes bandaged, and
Sebastian Paz took my hand to guide me. Eight flights of stairs we
mounted before the hubbub of many voices and the tinkle of a
guitar saluted my ears; two more, and the hubbub grew louder;
another, and it grew obstreperous, deafening. At the head of the
twelfth flight one of our guides rapped on a door; the noise died
down suddenly; a bolt was shot back and the bandage dragged from
my eyes.
I found myself blinking and staring across a room filled with
tobacco-smoke, and upon a company which at first glance I took for
a crew of demons. They were, in fact, a students' chorus—young
men in black, with black silk masks covering the upper half of their
faces. All wore the same uniform—black tunic, short black cloak,
knee-breeches, and stockings. Some squatted on the floor, two lolled
on a divan by the window—each with a guitar across his knees. The
man who had opened to us held a tambourine, and he alone wore a
little round cap. The others wore black cocked hats, or had flung
them off for better ease. In a deep armchair beside the fireplace sat
a stiff-backed, middle-aged woman in black—a duenna evidently—
who regarded us with eyes like large black beads, but did not
interrupt her knitting. In the corner behind the door stood a bed,
with a crucifix above it: and on the bed, between two crates, the
one of them heaped with flowers, sat a young woman dangling a
pretty pair of feet and smoking a cigarette while she made up a
posy.
In spite of their masks one could tell that all the men were young—
mere lads, indeed. And if this were Luisa, Fuentes had slandered her
sorely. She seemed scarcely eighteen—and we had taken her, too, at
unawares, when a woman forgets for a moment her endless vigilant
parry against Time. She tossed her posy into the half-filled basket,
clapped her hands, and sprang off the bed.
"Two new recruits! Bravo, Sebastianillo!"
With that, as she stepped gaily forward, her eyes fell on Fuentes,
and she swayed and fell back a pace, catching at the foot of the
bed.
"Don Eugenio!"
"Your servant, Señorita." He bowed elaborately and coldly. "You keep
the lamp burning, and I accepted its invitation. Your cheeks, too,
Señorita, keep the old colour. I congratulate you—and you, Doña
Isabel." He bowed to the old lady. "To live with youth—that is the
way to live always young."
She had moved forward again, as if to take him by both hands: but
faltered. "Yes, we have kept the lamp burning, Don Eugenio," she
answered with a voice curiously strained. "My friends"—she turned
to the young men—"rise and salute our guest of guests, Don
Eugenio Fuentes!"
"Fuentes!"
"What are you telling us, Luisa? The Fuentes? But it is impossible!"
"Impossible! Fuentes comes no more to Salamanca."
Nevertheless all had sprung to their feet, and Fuentes
comprehended them all in an ironical bow.
"That is the name by which I call myself, Sirs, since leaving the
University."
Luisa made a dumb signal, and one of the youths handed him a
guitar. He struck but one chord to assure himself of its tune—
"There's one that lives in Salamanca
All up a dozen flights of stairs;
There with the sparrows, night and morning,
Under the roof she chirps her prayers.
They say her wisdom comes from heaven—
Will you not take a guitar, Señorita, and help me with the old song?
So near the clouds and chimneys meet—
I rather think Luisa's sparrows
Fetch it aloft there from the street!"
Above all things women suspect and fear irony: it is not one of their
weapons. Luisa glanced at Fuentes doubtfully, I could see, and with
some pain in her doubt. But it was the old song, after all, and he
was singing it de bon cœur. She caught up a guitar and chimed in
with the second verse, taking up the soprano's part, while he at
once obeyed and dropped from treble to alto—
Which will you have? In la Verdura
Pretty Luisa keeps a stall:
Hands you a rose for your peseta,
Nothing to pay but a thorn—that's all!
King of her love, with no Prime Minister,
Lord of an attic blithe I'd reign.
But ay de mil! from here to Finisterre
Pretty Luisa loves all Spain.
His eyes, as he sang, were fastened on young Sebastian Paz, and
she, noting them, played the verse to its ringing close, turned
abruptly, and laid the guitar on the bed between the flower-baskets.
SHE CAUGHT UP A GUITAR AND CHIMED IN.
"But I think it is business brings you here, Don Eugenio."
He had stepped to the open lattice, and with an upward glance at
the lamp, burning steadily in the windless air, leaned on the sill and
looked out over the city. Somewhere below by the waterside a dull
noise sounded—the thud of a falling beam. The French down there
were working by lantern-light, clearing away the houses from their
fortifications.
"Yes, I come on business, and from Lord Wellington. The good
citizens in Salamanca have ceased to write."
"And small blame to them," one of the young men answered.
"Small blame to them, I agree. And yet they must send news—this
time to Lord Wellington, who knows better than to print it."
His eyes interrogated Luisa, who raised hers at length to meet them.
"That will not be easy," said she, with a pucker of her pretty
forehead. "They are scared and afraid for their heads: nevertheless,
Don Eugenio might bring back their confidence, if only we can bring
him face to face with them." She seated herself on the bed's edge
and mused awhile with her hands in her lap.
"You know where to find them?" asked Fuentes, addressing the
company in general.
"Oh, yes, Señor—assuredly we know where to find them!" answered
one or two.
"Then the whole thing is very simple. You must let me join your
choir, gentlemen."
"Yes, yes, that is simple enough," put in Luisa impatiently: "the more
so, as our chorus is popular not only in the taverns, but at the
French officers' messes. But these spies of ours are slow and dull to
a degree: I think sometimes it takes a quite special clumsiness to be
a clerk of the arsenal or to swindle the country in the military stores.
We can get you into communication with them, Don Eugenio: but
how are they to pass their information to you? They are born
bunglers, and the French begin to use their eyes." She pursed her
lips for a moment. "Is your friend new to this work?" she asked,
suddenly turning toward me a gaze of frank inspection.
Fuentes smiled. "You would not say so, Señorita, were I free to tell
you his name."
"As for that," said I, "where Señor Don Eugenio entrusts his secret I
may not hesitate to entrust mine. My name is Manuel MacNeill,
Señorita, and I kiss your hands and am at your service."
Luisa rose and dropped me a very stately curtsey. "Happy were I,
Don Manuel MacNeill, to welcome you, even if you did not solve our
difficulty. You are clever at disguises, I have been told. Well, I have a
disguise for you—though not, to be sure, a pleasant one."
"I take the downs with the ups," said I.
"Well, then, Don Diego here is an artist. He can paint you a bunch of
grapes so that the birds come to peck at it: moreover, he has studied
at the hospital. We must find you a suit of rags, Sir, and Don Diego
shall paint you as full of sores as Lazarus."
"And after that?"
"After that you will go to the porch of the New Cathedral, to the
shady side of it—look you how I study your comfort—facing on the
Square of the Old College: and there you shall collect the alms of the
charitable. Many things, I am told, find their way into a beggar's
hat."
"Señorita," said Fuentes gravely, with a glance up at the lamp, "it
was a good star that led us here to-night."
"The star, as you call it, has not failed in all these years," she
answered, with a look of timid appeal which hardened to one of
defiance.
"Nay," answered he coldly and lightly, "I never doubted it would—
while there was oil to feed it."
On the morrow, then, I took up my station by the porch of the
Cathedral, with a highly artistic wound in my left leg, a shade over
my right eye, and beside me a crutch and a ragged cap. The first
day brought me coppers only: but late on the second afternoon a
stout citizen, pausing on the steps and catching his breath
asthmatically before entering the Cathedral, dropped a paper pellet
in with his penny. On the third day it began to rain pellets, and I
drank that night to the assured success of our campaign.
I saw nothing of Fuentes. It had been agreed between us that I
should play my part in my own fashion, and I played it so thoroughly
as to take lodgings in the beggars' quarter, in a thieves' den—it was
little better—off the Street of the Rosary. It was enough for me that,
however Fuentes went about the sowing, the harvest kept pouring
in. As for the Street of the Virgins, I had been brought to it and had
quitted it in the dark, and it is a question if by daylight I could have
found it again. At any rate, I did not try.
But on the fourth day, at about five in the afternoon, as the day's
heat began to grow tolerable, I caught sight of Luisa herself picking
her way towards the Cathedral porch along the pavement under the
façade of the University. Before entering the great doors she paused
on the step beside me, bent to drop a coin into my cap, and
whispered—
"When I come out, follow me."
She passed on into the Cathedral and did not reappear for a quarter
of an hour, perhaps. In this time I had made up my mind that,
whatever the risk of my obeying her, she had probably weighed it
against some risk more urgent, and perhaps brought the message
direct from Fuentes. So when she came forth, and after pausing a
moment to readjust her mantilla, tripped down the steps and away
to the left down the street leading to the Porta del Rio, I picked up
my crutch, yawned, shook the coppers in my wallet, and hobbled
after her at a decent distance.
All the way I kept my eyes open and my ears too. In the streets
around the Porta del Rio the city's traffic was beginning to flow again
after the day's siesta: but I made pretty sure that we were not being
tracked. Through half-a-dozen streets she led me, and so to one
which I supposed to be the Street of the Virgins, and to a door
which I recognised for that to which Fuentes had brought me four
nights ago.
She had already knocked and been admitted: but the door opened
again as I came abreast of it, and I stepped past the porter into the
passage. Luisa stood half-way up the first flight of stairs under a
sunny window and beckoned, and aloft I climbed after her to her
attic. With her hand on the latch of her own door, she turned.
"You will find your clothes within," she said, and opened the door for
me to pass. "Dress—dress with speed—and find Don Eugenio. Your
work is done, and you must both be beyond the bridge before
sunset."
"Is there treachery, Señorita?" I asked.
"There is treachery of a kind, but not of the kind you guess. It is
important that Don Eugenio should be beyond the bridge to-night.
Your beasts at the Four Crowns are ready saddled. Find your friend,
and help him to go with all speed."
"But where shall I find him, Señorita? I have not set eyes on him for
three or four days."
"Yet he has done his work surely, has he not?"
"Far better than I could have hoped."
"You ask where he is to be found? But where else than by the
Archbishop's College, near by where the French have pulled down
his own College of San Lorenzo, and are destroying more? You
men!" She broke out into sudden passionate contempt. "The past is
all you have eyes for—the poor, wild, blundering past. You have no
eyes for the present, and with the past you poison its living joy. We
women cannot be always seventeen: yet because we are not, you
kill us—you kill us, I say!" Then, while I stared at her in downright
amaze, "Go, dress!" she cried, thrusting me into the room. "In your
coat you will find two letters. That without address you will give to
Don Eugenio when you find him: that which is marked with a cross
you will hand to him when you shall have passed the bridge—on no
account before. And now be quick, I beseech you: for this one room
is all my house."
Almost she thrust me within, and closed the door gently upon me.
When I emerged, in my right and proper clothes, it was to find her
yet waiting there upon the landing.
"I thank you for your speed, Señor Don Manuel; for I, too, am in
haste to change my dress: and my dress will require care to-night,
since I go to a masquerade." She gave me her hand. "Farewell,
friend!" she said.
I found Don Eugenio behind the College of the Archbishop, seated
on a mound and watching the French sappers at their work. I gave
him Luisa's letter.
"The wench," said he calmly, having read it, "is a born conspirator.
She cannot be happy unless she has a card hidden even from her
fellow-plotters. Still, it is usually safe to follow her advice. Our work
is pretty thoroughly done, I fancy?"
I nodded.
"We will see to our beasts then."
"She tells me they are ready saddled."
"Saints! She is in a hurry, that girl! Ah, well, then let us go and ask
no questions."
We found our mare and mule, paid our reckoning, and rode forth
from Salamanca. At the bridge-end we showed the passports, and
were bidden to go in peace. As we climbed the hill beyond, I handed
Fuentes Luisa's second letter.
"She bade me deliver it here," I explained.
He read it, turned in his saddle, and looked back towards the twilit
sky. "A likely tale," said he, crushing the letter into his pocket.

Scarcely a year later—to be precise, on the 17th of June, 1812—the


Allied forces crossed the fords above and below Salamanca, and
invested the fortifications which still commanded the bridge. In the
suburbs and outlying quarters the inhabitants lit up their houses
and, cheering and weeping, thronged the streets to press the hands
of the deliverers.
On the 27th the forts fell, and these scenes were renewed. I was
passing through the Plaza Mayor that night, about eight o'clock,
when a man plucked me by the sleeve, and, turning in the light of a
bonfire, I confronted Fuentes. I had not seen him since our return to
Lisbon: and his face, in the bonfire's glare, seemed to me to have
aged woefully.
"The shells may have spared her house," said he. "Do you care to go
with me and see what remains of it?"
He linked his arm in mine. We dived into the dark streets together.
The Street of the Virgins had suffered from the Allies' artillery, and
we picked our way over fallen chimney-stacks and heaps of rubble to
the remembered door. It stood open, no porter guarding it: but a
lamp smoked in the stairway, and by the light of it we mounted
together.
On the topmost landing all was dark, but here within the half-open
door a light shone. Fuentes tapped on the door and pressed it open.
From a deep armchair beside the empty fireplace a woman rose to
greet us. It was the duenna, Doña Isabel. Behind her in the open
window a lamp shone within a red shade, swaying a little in the
draught.
"I give you welcome, Sirs," quavered the old lady in a voice that
seemed to flicker, too, in the draught. "By the shouting I understood
that the forts have fallen and for some while I have been expecting
you.... It is dull up here, and a poor welcome for young gentlemen
since my darling died. But on such a night as this——"
She gazed around her, resting both hands on the arms of her chair.
"Luisa! Where is Luisa?" cried Fuentes sharply.
"They come very seldom now," pursued the old woman, not hearing
or not comprehending. "It is dull, you understand. You, Sir, are Don
Eugenio, are you not?" She nodded palsywise toward the white bed,
where a broken guitar lay between two baskets of withered flowers.
"I was to tell you——" She broke off and lifted a hand half-way to
her brow, but let it drop. "I was to tell you, if you came, that her
letter was true, and always the lamp had been lit for you only. It
burns still, you see. She loved you, my little one did; and she was
good—always, though she laughed, she was good."
Fuentes stepped to the bed and took the guitar in his hands. Some
blow had broken in the sounding-board, and one of the strings had
snapped.
"There is no blood upon it," went on the old woman in the same
tone that seemed pitilessly striving not to hurt. "The little one

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy