C++ coding standards: 101 rules, guidelines, and best practices [10. print ed.] 0321113586, 1291301321, 0076092018117, 9780321113580

Every software development team should have and follow a coding standard.It's even better when what the coding stan

293 92 1MB

English Pages XV, 220 Seiten [239] Year 2004;2011

Report DMCA / Copyright

DOWNLOAD PDF FILE

Table of contents :
Cover......Page 1
Contents......Page 8
Preface......Page 12
Organizational and Policy Issues......Page 18
0. Don't sweat the small stuff. (Or: Know what not to standardize.)......Page 19
1. Compile cleanly at high warning levels.......Page 21
2. Use an automated build system.......Page 24
3. Use a version control system.......Page 25
4. Invest in code reviews.......Page 26
Design Style......Page 28
5. Give one entity one cohesive responsibility.......Page 29
6. Correctness, simplicity, and clarity come first.......Page 30
7. Know when and how to code for scalability.......Page 31
8. Don't optimize prematurely.......Page 33
9. Don't pessimize prematurely.......Page 35
10. Minimize global and shared data.......Page 36
11. Hide information.......Page 37
12. Know when and how to code for concurrency.......Page 38
13. Ensure resources are owned by objects. Use explicit RAII and smart pointers.......Page 41
Coding Style......Page 44
14. Prefer compile- and link-time errors to run-time errors.......Page 45
15. Use const proactively.......Page 47
16. Avoid macros.......Page 49
17. Avoid magic numbers.......Page 51
18. Declare variables as locally as possible.......Page 52
19. Always initialize variables.......Page 53
20. Avoid long functions. Avoid deep nesting.......Page 55
21. Avoid initialization dependencies across compilation units.......Page 56
22. Minimize definitional dependencies. Avoid cyclic dependencies.......Page 57
23. Make header files self-sufficient.......Page 59
24. Always write internal # include guards. Never write external # include guards.......Page 60
Functions and Operators......Page 62
25. Take parameters appropriately by value, (smart) pointer, or reference.......Page 63
26. Preserve natural semantics for overloaded operators.......Page 64
27. Prefer the canonical forms of arithmetic and assignment operators.......Page 65
28. Prefer the canonical form of ++ and --. Prefer calling the prefix forms.......Page 67
29. Consider overloading to avoid implicit type conversions.......Page 68
30. Avoid overloading &&, ||, or, (comma).......Page 69
31. Don't write code that depends on the order of evaluation of function arguments.......Page 71
Class Design and Inheritance......Page 72
32. Be clear what kind of class you're writing.......Page 73
33. Prefer minimal classes to monolithic classes.......Page 74
34. Prefer composition to inheritance.......Page 75
35. Avoid inheriting from classes that were not designed to be base classes.......Page 77
36. Prefer providing abstract interfaces.......Page 79
37. Public inheritance is substitutability. Inherit, not to reuse, but to be reused.......Page 81
38. Practice safe overriding.......Page 83
39. Consider making virtual functions nonpublic, and public functions nonvirtual.......Page 85
40. Avoid providing implicit conversions.......Page 87
41. Make data members private, except in behaviorless aggregates (C-style structs).......Page 89
42. Don't give away your internals.......Page 91
43. Pimpl judiciously.......Page 93
44. Prefer writing nonmember nonfriend functions.......Page 96
45. Always provide new and delete together.......Page 97
46. If you provide any class- specific new, provide all of the standard forms ( plain, in-place, and nothrow).......Page 99
Construction, Destruction, and Copying......Page 102
47. Define and initialize member variables in the same order.......Page 103
48. Prefer initialization to assignment in constructors.......Page 104
49. Avoid calling virtual functions in constructors and destructors.......Page 105
50. Make base class destructors public and virtual, or protected and nonvirtual.......Page 107
51. Destructors, deallocation, and swap never fail.......Page 109
52. Copy and destroy consistently.......Page 111
53. Explicitly enable or disable copying.......Page 112
54. Avoid slicing. Consider Clone instead of copying in base classes.......Page 113
55. Prefer the canonical form of assignment.......Page 116
56. Whenever it makes sense, provide a no-fail swap (and provide it correctly).......Page 117
Namespaces and Modules......Page 120
57. Keep a type and its nonmember function interface in the same namespace.......Page 121
58. Keep types and functions in separate namespaces unless they're specifically intended to work together.......Page 123
59. Don't write namespace usings in a header file or before an #include.......Page 125
60. Avoid allocating and deallocating memory in different modules.......Page 128
61. Don't define entities with linkage in a header file.......Page 129
62. Don't allow exceptions to propagate across module boundaries.......Page 131
63. Use sufficiently portable types in a module's interface.......Page 133
Templates and Genericity......Page 136
64. Blend static and dynamic polymorphism judiciously.......Page 137
65. Customize intentionally and explicitly.......Page 139
66. Don't specialize function templates.......Page 143
67. Don't write unintentionally nongeneric code.......Page 145
Error Handling and Exceptions......Page 146
68. Assert liberally to document internal assumptions and invariants.......Page 147
69. Establish a rational error handling policy, and follow it strictly.......Page 149
70. Distinguish between errors and non-errors.......Page 151
71. Design and write error-safe code.......Page 154
72. Prefer to use exceptions to report errors.......Page 157
73. Throw by value, catch by reference.......Page 161
74. Report, handle, and translate errors appropriately.......Page 162
75. Avoid exception specifications.......Page 163
STL: Containers......Page 166
76. Use vector by default. Otherwise, choose an appropriate container.......Page 167
77. Use vector and string instead of arrays.......Page 169
78. Use vector (and string::c_str) to exchange data with non-C++ APIs.......Page 170
79. Store only values and smart pointers in containers.......Page 171
80. Prefer push_back to other ways of expanding a sequence.......Page 172
81. Prefer range operations to single-element operations.......Page 173
82. Use the accepted idioms to really shrink capacity and really erase elements.......Page 174
STL: Algorithms......Page 176
83. Use a checked STL implementation.......Page 177
84. Prefer algorithm calls to handwritten loops.......Page 179
85. Use the right STL search algorithm.......Page 182
86. Use the right STL sort algorithm.......Page 183
87. Make predicates pure functions.......Page 185
88. Prefer function objects over functions as algorithm and comparer arguments.......Page 187
89. Write function objects correctly.......Page 189
Type Safety......Page 190
90. Avoid type switching; prefer polymorphism.......Page 191
91. Rely on types, not on representations.......Page 193
92. Avoid using reinterpret_cast.......Page 194
93. Avoid using static_cast on pointers.......Page 195
94. Avoid casting away const.......Page 196
95. Don't use C-style casts.......Page 197
96. Don't memcpy or memcmp non-PODs.......Page 199
97. Don't use unions to reinterpret representation.......Page 200
98. Don't use varargs (ellipsis).......Page 201
99. Don't use invalid objects. Don't use unsafe functions.......Page 202
100. Don't treat arrays polymorphically.......Page 203
Bibliography......Page 204
Summary of Summaries......Page 212
A......Page 226
C......Page 227
D......Page 229
F......Page 230
I......Page 231
M......Page 232
O......Page 233
Q......Page 234
S......Page 235
U......Page 236
Z......Page 237
Recommend Papers

C++ coding standards: 101 rules, guidelines, and best practices [10. print ed.]
 0321113586, 1291301321, 0076092018117, 9780321113580

  • 0 0 0
  • Like this paper and download? You can publish your own PDF file online for free in a few minutes! Sign Up
File loading please wait...
Citation preview

C++ Coding Standards

The C++ In-Depth Series Bjarne Stroustrup, Editor “I have made this letter longer than usual, because I lack the time to make it short.” —BLAISE PASCAL

T

he advent of the ISO/ANSI C++ standard marked the beginning of a new era for C++ programmers. The standard offers many new facilities and opportunities, but how can a real-world programmer find the time to discover the key nuggets of wisdom within this mass of information? The C++ In-Depth Series minimizes learning time and confusion by giving programmers concise, focused guides to specific topics. Each book in this series presents a single topic, at a technical level appropriate to that topic. The Series’ practical approach is designed to lift professionals to their next level of programming skills. Written by experts in the field, these short, in-depth monographs can be read and referenced without the distraction of unrelated material. The books are cross-referenced within the Series, and also reference The C++ Programming Language by Bjarne Stroustrup. As you develop your skills in C++, it becomes increasingly important to separate essential information from hype and glitz, and to find the in-depth content you need in order to grow. The C++ In-Depth Series provides the tools, concepts, techniques, and new approaches to C++ that will give you a critical edge.

Titles in the Series Accelerated C++: Practical Programming by Example, Andrew Koenig and Barbara E. Moo Applied C++: Practical Techniques for Building Better Software, Philip Romanik and Amy Muntz The Boost Graph Library: User Guide and Reference Manual, Jeremy G. Siek, Lie-Quan Lee, and Andrew Lumsdaine C++ Coding Standards: 101 Rules, Guidelines, and Best Practices, Herb Sutter and Andrei Alexandrescu C++ In-Depth Box Set, Bjarne Stroustrup, Andrei Alexandrescu, Andrew Koenig, Barbara E. Moo, Stanley B. Lippman, and Herb Sutter C++ Network Programming, Volume 1: Mastering Complexity with ACE and Patterns, Douglas C. Schmidt and Stephen D. Huston C++ Network Programming, Volume 2: Systematic Reuse with ACE and Frameworks, Douglas C. Schmidt and Stephen D. Huston C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond, David Abrahams and Aleksey Gurtovoy Essential C++, Stanley B. Lippman Exceptional C++: 47 Engineering Puzzles, Programming Problems, and Solutions, Herb Sutter Exceptional C++ Style: 40 New Engineering Puzzles, Programming Problems, and Solutions, Herb Sutter Modern C++ Design: Generic Programming and Design Patterns Applied, Andrei Alexandrescu More Exceptional C++: 40 New Engineering Puzzles, Programming Problems, and Solutions, Herb Sutter For more information, check out the series web site at www.awprofessional.com/series/indepth/

C++ Coding Standards 101 Rules, Guidelines, and Best Practices

Herb Sutter Andrei Alexandrescu

Boston

The authors and publisher have taken care in the preparation of this book, but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for incidental or consequential damages in connection with or arising out of the use of the information or programs contained herein. Publisher: John Wait Editor in Chief: Don O’Hagan Acquisitions Editor: Peter Gordon Editorial Assistant: Kim Boedigheimer Marketing Manager: Chanda Leary-Coutu Cover Designer: Chuti Prasertsith Managing Editor: John Fuller Project Editor: Lara Wysong Copy Editor: Kelli Brooks Manufacturing Buyer: Carol Melville The publisher offers excellent discounts on this book when ordered in quantity for bulk purchases or special sales, which may include electronic versions and/or custom covers and content particular to your business, training goals, marketing focus, and branding interests. For more information, please contact: U. S. Corporate and Government Sales (800) 382-3419 [email protected] For sales outside the U. S., please contact: International Sales [email protected] Visit us on the Web: www.awprofessional.com Library of Congress Cataloging-in-Publication Data: Sutter, Herb. C++ coding standards : 101 rules, guidelines, and best practices / Herb Sutter, Andrei Alexandrescu. p. cm. Includes bibliographical references and index. ISBN 0-321-11358-6 (pbk. : alk. paper) C++ (Computer program language) I. Alexandrescu, Andrei. II. Title. QA76.73.C153S85 2004 005.13'3—dc22 2004022605 Copyright © 2005 Pearson Education, Inc. All rights reserved. Printed in the United States of America. This publication is protected by copyright, and permission must be obtained from the publisher 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. For information regarding permissions, write to: Pearson Education, Inc. Rights and Contracts Department One Lake Street Upper Saddle River, NJ 07458 ISBN 0-321-11358-6 Text printed in the United States on recycled paper at Courier in Stoughton, Massachusetts. Third printing, February 2005

For the millions of current C++ programmers.

This page intentionally left blank

Contents

Preface Organizational and Policy Issues 0. 1. 2. 3. 4.

Don’t sweat the small stuff. (Or: Know what not to standardize.) Compile cleanly at high warning levels. Use an automated build system. Use a version control system. Invest in code reviews.

Design Style 5. 6. 7. 8. 9. 10. 11. 12. 13.

Give one entity one cohesive responsibility. Correctness, simplicity, and clarity come first. Know when and how to code for scalability. Don’t optimize prematurely. Don’t pessimize prematurely. Minimize global and shared data. Hide information. Know when and how to code for concurrency. Ensure resources are owned by objects. Use explicit RAII and smart pointers.

Coding Style 14. Prefer compile- and link-time errors to run-time errors. 15. Use const proactively. 16. Avoid macros.

xi 1 2 4 7 8 9

11 12 13 14 16 18 19 20 21 24

27 28 30 32

vii

viii 17. 18. 19. 20. 21. 22. 23. 24.

Contents

Avoid magic numbers. Declare variables as locally as possible. Always initialize variables. Avoid long functions. Avoid deep nesting. Avoid initialization dependencies across compilation units. Minimize definitional dependencies. Avoid cyclic dependencies. Make header files self-sufficient. Always write internal #include guards. Never write external #include guards.

Functions and Operators 25. 26. 27. 28. 29. 30. 31.

Take parameters appropriately by value, (smart) pointer, or reference. Preserve natural semantics for overloaded operators. Prefer the canonical forms of arithmetic and assignment operators. Prefer the canonical form of ++ and --. Prefer calling the prefix forms. Consider overloading to avoid implicit type conversions. Avoid overloading &&, ||, or , (comma) . Don’t write code that depends on the order of evaluation of function arguments.

Class Design and Inheritance 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46.

Be clear what kind of class you’re writing. Prefer minimal classes to monolithic classes. Prefer composition to inheritance. Avoid inheriting from classes that were not designed to be base classes. Prefer providing abstract interfaces. Public inheritance is substitutability. Inherit, not to reuse, but to be reused. Practice safe overriding. Consider making virtual functions nonpublic, and public functions nonvirtual. Avoid providing implicit conversions. Make data members private, except in behaviorless aggregates (C-style structs). Don’t give away your internals. Pimpl judiciously. Prefer writing nonmember nonfriend functions. Always provide new and delete together. If you provide any class-specific new, provide all of the standard forms (plain, in-place, and nothrow).

34 35 36 38 39 40 42 43

45 46 47 48 50 51 52 54

55 56 57 58 60 62 64 66 68 70 72 74 76 79 80 82

Contents

Construction, Destruction, and Copying 47. 48. 49. 50. 51. 52. 53. 54. 55. 56.

Define and initialize member variables in the same order. Prefer initialization to assignment in constructors. Avoid calling virtual functions in constructors and destructors. Make base class destructors public and virtual, or protected and nonvirtual. Destructors, deallocation, and swap never fail. Copy and destroy consistently. Explicitly enable or disable copying. Avoid slicing. Consider Clone instead of copying in base classes. Prefer the canonical form of assignment. Whenever it makes sense, provide a no-fail swap (and provide it correctly).

Namespaces and Modules 57. Keep a type and its nonmember function interface in the same namespace. 58. Keep types and functions in separate namespaces unless they’re specifically intended to work together. 59. Don’t write namespace usings in a header file or before an #include. 60. Avoid allocating and deallocating memory in different modules. 61. Don’t define entities with linkage in a header file. 62. Don’t allow exceptions to propagate across module boundaries. 63. Use sufficiently portable types in a module’s interface.

Templates and Genericity 64. 65. 66. 67.

Blend static and dynamic polymorphism judiciously. Customize intentionally and explicitly. Don’t specialize function templates. Don’t write unintentionally nongeneric code.

Error Handling and Exceptions 68. 69. 70. 71. 72. 73. 74. 75.

Assert liberally to document internal assumptions and invariants. Establish a rational error handling policy, and follow it strictly. Distinguish between errors and non-errors. Design and write error-safe code. Prefer to use exceptions to report errors. Throw by value, catch by reference. Report, handle, and translate errors appropriately. Avoid exception specifications.

ix 85 86 87 88 90 92 94 95 96 99 100

103 104 106 108 111 112 114 116

119 120 122 126 128

129 130 132 134 137 140 144 145 146

x

Contents

STL: Containers 76. 77. 78. 79. 80. 81. 82.

Use vector by default. Otherwise, choose an appropriate container. Use vector and string instead of arrays. Use vector (and string::c_str) to exchange data with non-C++ APIs. Store only values and smart pointers in containers. Prefer push_back to other ways of expanding a sequence. Prefer range operations to single-element operations. Use the accepted idioms to really shrink capacity and really erase elements.

STL: Algorithms 83. 84. 85. 86. 87. 88. 89.

Use a checked STL implementation. Prefer algorithm calls to handwritten loops. Use the right STL search algorithm. Use the right STL sort algorithm. Make predicates pure functions. Prefer function objects over functions as algorithm and comparer arguments. Write function objects correctly.

Type Safety 90. Avoid type switching; prefer polymorphism. 91. Rely on types, not on representations. 92. Avoid using reinterpret_cast. 93. Avoid using static_cast on pointers. 94. Avoid casting away const. 95. Don’t use C-style casts. 96. Don’t memcpy or memcmp non-PODs. 97. Don’t use unions to reinterpret representation. 98. Don’t use varargs (ellipsis). 99. Don’t use invalid objects. Don’t use unsafe functions. 100.Don’t treat arrays polymorphically.

149 150 152 153 154 155 156 157

159 160 162 165 166 168 170 172

173 174 176 177 178 179 180 182 183 184 185 186

Bibliography

187

Summary of Summaries

195

Index

209

Preface Get into a rut early: Do the same process the same way. Accumulate idioms. Standardize. The only difference(!) between Shakespeare and you was the size of his idiom list—not the size of his vocabulary. — Alan Perlis [emphasis ours]

The best thing about standards is that there are so many to choose from. — Variously attributed

We want to provide this book as a basis for your team’s coding standards for two principal reasons: x A coding standard should reflect the community’s best tried-and-true experience: It should contain proven idioms based on experience and solid understanding of the language. In particular, a coding standard should be based firmly on the extensive and rich software development literature, bringing together rules, guidelines, and best practices that would otherwise be left scattered throughout many sources. x Nature abhors a vacuum: If you don’t consciously set out reasonable rules, usually someone else will try to push their own set of pet rules instead. A coding standard made that way usually has all of the least desirable properties of a coding standard; for example, many such standards try to enforce a minimalistic Cstyle use of C++. Many bad coding standards have been set by people who don’t understand the language well, don’t understand software development well, or try to legislate too much. A bad coding standard quickly loses credibility and at best even its valid guidelines are liable to be ignored by disenchanted programmers who dislike or disagree with its poorer guidelines. That’s “at best”—at worst, a bad standard might actually be enforced.

xi

xii

Preface

How to Use This Book Think. Do follow good guidelines conscientiously; but don’t follow them blindly. In this book’s Items, note the Exceptions clarifying the less common situations where the guidance may not apply. No set of guidelines, however good (and we think these ones are), should try to be a substitute for thinking. Each development team is responsible for setting its own standards, and for setting them responsibly. That includes your team. If you are a team lead, involve your team members in setting the team’s standards; people are more likely to follow standards they view as their own than they are to follow a bunch of rules they feel are being thrust upon them. This book is designed to be used as a basis for, and to be included by reference in, your team’s coding standards. It is not intended to be the Last Word in coding standards, because your team will have additional guidelines appropriate to your particular group or task, and you should feel free to add those to these Items. But we hope that this book will save you some of the work of (re)developing your own, by documenting and referencing widely-accepted and authoritative practices that apply nearly universally (with Exceptions as noted), and so help increase the quality and consistency of the coding standards you use. Have your team read these guidelines with their rationales (i.e., the whole book, and selected Items’ References to other books and papers as needed), and decide if there are any that your team simply can’t live with (e.g., because of some situation unique to your project). Then commit to the rest. Once adopted, the team’s coding standards should not be violated except after consulting with the whole team. Finally, periodically review your guidelines as a team to include practical experience and feedback from real use.

Coding Standards and You Good coding standards can offer many interrelated advantages: x Improved code quality: Encouraging developers to do the right things in a consistent way directly works to improve software quality and maintainability. x Improved development speed: Developers don’t need to always make decisions starting from first principles. x Better teamwork: They help reduce needless debates on inconsequential issues and make it easier for teammates to read and maintain each other’s code. x Uniformity in the right dimension: This frees developers to be creative in directions that matter.

Preface

xiii

Under stress and time pressure, people do what they’ve been trained to do. They fall back on habit. That’s why ER units in hospitals employ experienced, trained personnel; even knowledgeable beginners would panic. As software developers, we routinely face enormous pressure to deliver tomorrow’s software yesterday. Under schedule pressure, we do what we are trained to do and are used to doing. Sloppy programmers who in normal times don’t know good practices of software engineering (or aren’t used to applying them) will write even sloppier and buggier code when pressure is on. Conversely, programmers who form good habits and practice them regularly will keep themselves organized and deliver quality code, fast. The coding standards introduced by this book are a collection of guidelines for writing high-quality C++ code. They are the distilled conclusions of a rich collective experience of the C++ community. Much of this body of knowledge has only been available in bits and pieces spread throughout books, or as word-of-mouth wisdom. This book’s intent is to collect that knowledge into a collection of rules that is terse, justified, and easy to understand and follow. Of course, one can write bad code even with the best coding standards. The same is true of any language, process, or methodology. A good set of coding standards fosters good habits and discipline that transcend mere rules. That foundation, once acquired, opens the door to higher levels. There’s no shortcut; you have to develop vocabulary and grammar before writing poetry. We just hope to make that easier. We address this book to C++ programmers of all levels: If you are an apprentice programmer, we hope you will find the rules and their rationale helpful in understanding what styles and idioms C++ supports most naturally. We provide a concise rationale and discussion for each rule and guideline to encourage you to rely on understanding, not just rote memorization. For the intermediate or advanced programmer, we have worked hard to provide a detailed list of precise references for each rule. This way, you can do further research into the rule’s roots in C++’s type system, grammar, and object model. At any rate, it is very likely that you work in a team on a complex project. Here is where coding standards really pay off—you can use them to bring the team to a common level and provide a basis for code reviews.

About This Book We have set out the following design goals for this book: x Short is better than long: Huge coding standards tend to be ignored; short ones get read and used. Long Items tend to be skimmed; short ones get read and used.

xiv

Preface

x Each Item must be noncontroversial: This book exists to document widely agreedupon standards, not to invent them. If a guideline is not appropriate in all cases, it will be presented that way (e.g., “Consider X…” instead of “Do X…”) and we will note commonly accepted exceptions. x Each Item must be authoritative: The guidelines in this book are backed up by references to existing published works. This book is intended to also provide an index into the C++ literature. x Each Item must need saying: We chose not to define new guidelines for things that you’ll do anyway, that are already enforced or detected by the compiler, or that are already covered under other Items. Example: “Don’t return a pointer/reference to an automatic variable” is a good guideline, but we chose not to include it in this book because all of the compilers we tried already emit a warning for this, and so the issue is already covered under the broader Item 1, “Compile cleanly at high warning levels.” Example: “Use an editor (or compiler, or debugger)” is a good guideline, but of course you’ll use those tools anyway without being told; instead, we spend two of our first four Items on “Use an automated build system” and “Use a version control system.” Example: “Don’t abuse goto” is a great Item, but in our experience programmers universally know this, and it doesn’t need saying any more. Each Item is laid out as follows: x Item title: The simplest meaningful sound bite we could come up with as a mnemonic for the rule. x Summary: The most essential points, briefly stated. x Discussion: An extended explanation of the guideline. This often includes brief rationale, but remember that the bulk of the rationale is intentionally left in the References. x Examples (if applicable): Examples that demonstrate a rule or make it memorable. x Exceptions (if applicable): Any (and usually rare) cases when a rule doesn’t apply. But beware the trap of being too quick to think: “Oh, I’m special; this doesn’t apply in my situation”—that rationalization is common, and commonly wrong. x References: See these parts of the C++ literature for the full details and analysis. In each section, we chose to nominate a “most valuable Item.” Often, it’s the first Item in a section, because we tried to put important Items up front in each part; but

Preface

xv

other times an important Item couldn’t be put up front, for flow or readability reasons, and we felt the need to call it out for special attention in this way.

Acknowledgments Many thanks to series editor Bjarne Stroustrup, to editors Peter Gordon and Debbie Lafferty, and to Tyrrell Albaugh, Kim Boedigheimer, John Fuller, Bernard Gaffney, Curt Johnson, Chanda Leary-Coutu, Charles Leddy, Heather Mullane, Chuti Prasertsith, Lara Wysong, and the rest of the Addison-Wesley team for their assistance and persistence during this project. They are a real pleasure to work with. Inspiration for some of the “sound bites” came from many sources, including the playful style of [Cline99], the classic import this of [Peters99], and the legendary and eminently quotable Alan Perlis. We especially want to thank the people whose technical feedback has helped to make many parts of this book better than they would otherwise have been. Series editor Bjarne Stroustrup’s incisive comments from concept all the way through to the final draft were heavily influential and led to many improvements. We want to give special thanks to Dave Abrahams, Marshall Cline, Kevlin Henney, Howard Hinnant, Jim Hyslop, Nicolai Josuttis, Jon Kalb, Max Khesin, Stan Lippman, Scott Meyers, and Daveed Vandevoorde for their active participation in review cycles and detailed comments on several drafts of this material. Other valuable comments and feedback were contributed by Chuck Allison, Samir Bajaj, Marc Barbour, Travis Brown, Neal Coombes, Damian Dechev, Steve Dewhurst, Peter Dimov, Attila Feher, Alan Griffiths, Michi Henning, James Kanze, Mat Marcus, Petru Marginean, Robert C. “Uncle Bob” Martin, Bartosz Milewski, Balog Pal, Jeff Peil, Peter Pirkelbauer, Vladimir Prus, Dan Saks, Luke Wagner, Matthew Wilson, and Leor Zolman. As usual, the remaining errors, omissions, and shameless puns are ours, not theirs.

Herb Sutter Andrei Alexandrescu Seattle, September 2004

This page intentionally left blank

Organizational and Policy Issues If builders built buildings the way programmers wrote programs, then the first woodpecker that came along would destroy civilization. —Gerald Weinberg

In the grand tradition of C and C++, we count the zero-based way. The prime directive, Item 0, covers what we feel is the most basic advice about coding standards. The rest of this introductory section goes on to target a small number of carefully selected basic issues that are mostly not directly about the code itself, but on essential tools and techniques for writing solid code. Our vote for the most valuable Item in this section goes to Item 0: Don’t sweat the small stuff. (Or: Know what not to standardize.)

1

2

C++ Coding Standards

0.

Don’t sweat the small stuff. (Or: Know what not to standardize.) Summary Say only what needs saying: Don’t enforce personal tastes or obsolete practices.

Discussion Issues that are really just personal taste and don’t affect correctness or readability don’t belong in a coding standard. Any professional programmer can easily read and write code that is formatted a little differently than they’re used to. Do use consistent formatting within each source file or even each project, because it’s jarring to jump around among several styles in the same piece of code. But don’t try to enforce consistent formatting across multiple projects or across a company. Here are several common issues where the important thing is not to set a rule but just to be consistent with the style already in use within the file you’re maintaining: x Don’t specify how much to indent, but do indent to show structure: Use any number of spaces you like to indent, but be consistent within at least each file. x Don’t enforce a specific line length, but do keep line lengths readable: Use any length of line you like, but don’t be excessive. Studies show that up to ten-word text widths are optimal for eye tracking. x Don’t overlegislate naming, but do use a consistent naming convention: There are only two must-dos: a) never use “underhanded names,” ones that begin with an underscore or that contain a double underscore; and b) always use ONLY_UPPERCASE_NAMES for macros and never think about writing a macro that is a common word or abbreviation (including common template parameters, such as T and U; writing #define T anything is extremely disruptive). Otherwise, do use consistent and meaningful names and follow a file’s or module’s convention. (If you can’t decide on your own naming convention, try this one: Name classes, functions, and enums LikeThis; name variables likeThis; name private member variables likeThis_; and name macros LIKE_THIS.) x Don’t prescribe commenting styles (except where tools extract certain styles into documentation), but do write useful comments: Write code instead of comments where possible (e.g., see Item 16). Don’t write comments that repeat the code; they get out of sync. Do write illuminating comments that explain approach and rationale. Finally, don’t try to enforce antiquated rules (see Examples 3 and 4) even if they once appeared in older coding standards.

Examples Example 1: Brace placement. There is no readability difference among:

Organizational and Policy Issues

3

void using_k_and_r_style() { // … } void putting_each_brace_on_its_own_line() { // … } void or_putting_each_brace_on_its_own_line_indented() { // … } Any professional programmer can easily read and write any of these styles without hardship. But do be consistent: Don’t just place braces randomly or in a way that obscures scope nesting, and try to follow the style already in use in each file. In this book, our brace placement choices are motivated by maximizing readability within our editorial constraints. Example 2: Spaces vs. tabs. Some teams legitimately choose to ban tabs (e.g., [BoostLRG]), on the grounds that tabs vary from editor to editor and, when misused, turn indenting into outdenting and nondenting. Other equally respectable teams legitimately allow tabs, adopting disciplines to avoid their potential drawbacks. Just be consistent: If you do allow tabs, ensure it is never at the cost of code clarity and readability as team members maintain each other’s code (see Item 6). If you don’t allow tabs, allow editors to convert spaces to tabs when reading in a source file so that users can work with tabs while in the editor, but ensure they convert the tabs back to spaces when writing the file back out. Example 3: Hungarian notation. Notations that incorporate type information in variable names have mixed utility in type-unsafe languages (notably C), are possible but have no benefits (only drawbacks) in object-oriented languages, and are impossible in generic programming. Therefore, no C++ coding standard should require Hungarian notation, though a C++ coding standard might legitimately choose to ban it. Example 4: Single entry, single exit (“SESE”). Historically, some coding standards have required that each function have exactly one exit, meaning one return statement. Such a requirement is obsolete in languages that support exceptions and destructors, where functions typically have numerous implicit exits. Instead, follow standards like Item 5 that directly promote simpler and shorter functions that are inherently easier to understand and to make error-safe.

References [BoostLRG] • [Brooks95] §12 • [Constantine95] §29 • [Keffer95] p. 1 • [Kernighan99] §1.1, §1.3, §1.6-7 • [Lakos96] §1.4.1, §2.7 • [McConnell93] §9, §19 • [Stroustrup94] §4.2-3 • [Stroustrup00] §4.9.3, §6.4, §7.8, §C.1 • [Sutter00] §6, §20 • [SuttHysl01]

4

C++ Coding Standards

1.

Compile cleanly at high warning levels. Summary Take warnings to heart: Use your compiler’s highest warning level. Require clean (warning-free) builds. Understand all warnings. Eliminate warnings by changing your code, not by reducing the warning level.

Discussion Your compiler is your friend. If it issues a warning for a certain construct, often there’s a potential problem in your code. Successful builds should be silent (warning-free). If they aren’t, you’ll quickly get into the habit of skimming the output, and you will miss real problems. (See Item 2.) To get rid of a warning: a) understand it; and then b) rephrase your code to eliminate the warning and make it clearer to both humans and compilers that the code does what you intended. Do this even when the program seemed to run correctly in the first place. Do this even when you are positive that the warning is benign. Even benign warnings can obscure later warnings pointing to real dangers.

Examples Example 1: A third-party header file. A library header file that you cannot change could contain a construct that causes (probably benign) warnings. Then wrap the file with your own version that #includes the original header and selectively turns off the noisy warnings for that scope only, and then #include your wrapper throughout the rest of your project. Example (note that the warning control syntax will vary from compiler to compiler): // File: myproj/my_lambda.h -- wraps Boost’s lambda.hpp // Always include this file; don’t use lambda.hpp directly. // NOTE: Our build now automatically checks “grep lambda.hpp “. // Boost.Lambda produces noisy compiler warnings that we know are innocuous. // When they fix it we’ll remove the pragmas below, but this header will still exist. // #pragma warning(push) // disable for this header only #pragma warning(disable:4512) #pragma warning(disable:4180) #include #pragma warning(pop) // restore original warning level

Organizational and Policy Issues

5

Example 2: “Unused function parameter.” Check to make sure you really didn’t mean to use the function parameter (e.g., it might be a placeholder for future expansion, or a required part of a standardized signature that your code has no use for). If it’s not needed, simply delete the name of a function parameter: // … inside a user-defined allocator that has no use for the hint … // warning: “unused parameter ‘localityHint’” pointer allocate( size_type numObjects, const void *localityHint = 0 ) { return static_cast( mallocShared( numObjects * sizeof(T) ) ); } // new version: eliminates warning pointer allocate( size_type numObjects, const void * /* localityHint */ = 0 ) { return static_cast( mallocShared( numObjects * sizeof(T) ) ); } Example 3: “Variable defined but never used.” Check to make sure you really didn’t mean to reference the variable. (An RAII stack-based object often causes this warning spuriously; see Item 13.) If it’s not needed, often you can silence the compiler by inserting an evaluation of the variable itself as an expression (this evaluation won’t impact run-time speed): // warning: “variable ‘lock’ is defined but never used” void Fun() { Lock lock; // … } // new version: probably eliminates warning void Fun() { Lock lock; lock; // … } Example 4: “Variable may be used without being initialized.” Initialize the variable (see Item 19). Example 5: “Missing return.” Sometimes the compiler asks for a return statement even though your control flow can never reach the end of the function (e.g., infinite loop, throw statements, other returns). This can be a good thing, because sometimes you only think that control can’t run off the end. For example, switch statements that

6

C++ Coding Standards do not have a default are not resilient to change and should have a default case that does assert( false ) (see also Items 68 and 90): // warning: missing “return” int Fun( Color c ) { switch( c ) { case Red: return 2; case Green: return 0; case Blue: case Black: return 1; } } // new version: eliminates warning int Fun( Color c ) { switch( c ) { case Red: return 2; case Green: return 0; case Blue: case Black: return 1; default: assert( !”should never get here!” ); return -1; } }

// !”string” evaluates to false

Example 6: “Signed/unsigned mismatch.” It is usually not necessary to compare or assign integers with different signedness. Change the types of the variables being compared so that the types agree. In the worst case, insert an explicit cast. (The compiler inserts that cast for you anyway, and warns you about doing it, so you’re better off putting it out in the open.)

Exceptions Sometimes, a compiler may emit a tedious or even spurious warning (i.e., one that is mere noise) but offer no way to turn it off, and it might be infeasible or unproductive busywork to rephrase the code to silence the warning. In these rare cases, as a team decision, avoid tediously working around a warning that is merely tedious: Disable that specific warning only, disable it as locally as possible, and write a clear comment documenting why it was necessary.

References [Meyers97] §48 • [Stroustrup94] §2.6.2

Organizational and Policy Issues

2.

7

Use an automated build system. Summary Push the (singular) button: Use a fully automatic (“one-action”) build system that builds the whole project without user intervention.

Discussion A one-action build process is essential. It must produce a dependable and repeatable translation of your source files into a deliverable package. There is a broad range of automated build tools available, and no excuse not to use one. Pick one. Use it. We’ve seen organizations that neglect the “one-action” requirement. Some consider that a few mouse clicks here and there, running some utilities to register COM/CORBA servers, and copying some files by hand constitute a reasonable build process. But you don’t have time and energy to waste on something a machine can do faster and better. You need a one-action build that is automated and dependable. Successful builds should be silent, warning-free (see Item 1). The ideal build produces no noise and only one log message: “Build succeeded.” Have two build modes: Incremental and full. An incremental build rebuilds only what has changed since the last incremental or full build. Corollary: The second of two successive incremental builds should not write any output files; if it does, you probably have a dependency cycle (see Item 22), or your build system performs unnecessary operations (e.g., writes spurious temporary files just to discard them). A project can have different forms of full build. Consider parameterizing your build by a number of essential features; likely candidates are target architecture, debug vs. release, and breadth (essential files vs. all files vs. full installer). One build setting can create the product’s essential executables and libraries, another might also create ancillary files, and a full-fledged build might create an installer that comprises all your files, third-party redistributables, and installation code. As projects grow over time, so does the cost of not having an automated build. If you don’t use one from the start, you will waste time and resources. Worse still, by the time the need for an automated build becomes overwhelming, you will be under more pressure than at the start of the project. Large projects might have a “build master” whose job is to care for the build system.

References [Brooks95] §13, §19 • [Dewhurst03] §1 • [GnuMake] • [Stroustrup00] §9.1

8

C++ Coding Standards

3.

Use a version control system. Summary The palest of ink is better than the best memory (Chinese proverb): Use a version control system (VCS). Never keep files checked out for long periods. Check in frequently after your updated unit tests pass. Ensure that checked-in code does not break the build.

Discussion Nearly all nontrivial projects need more than one developer and/or take more than a week of work. On such projects, you will need to compare historical versions of the same file to determine when (and/or by whom) changes were introduced. You will need to control and manage source changes. When there are multiple developers, those developers will make changes in parallel, possibly to different parts of the same file at the same time. You need tools to automate checkout/versioning of file and, in some cases, merging of concurrent edits. A VCS automates and controls checkouts, versioning, and merging. A VCS will do it faster and more correctly than you could do it by hand. And you don’t have time to fiddle with administrivia—you have software to write. Even a single developer has “oops!” and “huh?” moments, and needs to figure out when and why a bug or change was introduced. So will you. A VCS automatically tracks the history of each file and lets you “turn the clock back.” The question isn’t whether you will want to consult the history, but when. Don’t break the build. The code in the VCS must always build successfully. The broad range of VCS offerings leaves no excuse not to use one. The least expensive and most popular is cvs (see References). It is a flexible tool, featuring TCP/IP access, optional enhanced security (by using the secure shell ssh protocol as a backend), excellent administration through scripting, and even a graphical interface. Many other VCS products either treat cvs as a standard to emulate, or build new functionality on top of it.

Exceptions A project with one programmer that takes about a week from start to finish probably can live without a VCS.

References [BetterSCM] • [Brooks95] §11, §13 • [CVS]

Organizational and Policy Issues

4.

9

Invest in code reviews. Summary Re-view code: More eyes will help make more quality. Show your code, and read others’. You’ll all learn and benefit.

Discussion A good code review process benefits your team in many ways. It can: x Increase code quality through beneficial peer pressure. x Find bugs, non-portable code (if applicable), and potential scaling problems. x Foster better design and implementation through cross-breeding of ideas. x Bring newer teammates and beginners up to speed. x Develop common values and a sense of community inside the team. x Increase meritocracy, confidence, motivation, and professional pride. Many shops neither reward quality code and quality teams nor invest time and money encouraging them. We hope we won’t have to eat our words a couple of years from now, but we feel that the tide is slowly changing, due in part to an increased need for safe and secure software. Code reviews help foster exactly that, in addition to being an excellent (and free!) method of in-house training. Even if your employer doesn’t yet support a code reviewing process, do increase management awareness (hint: to start, show them this book) and do your best to make time and conduct reviews anyway. It is time well spent. Make code reviews a routine part of your software development cycle. If you agree with your teammates on a reward system based on incentives (and perhaps disincentives), so much the better. Without getting too formalistic, it’s best to get code reviews in writing—a simple e-mail can suffice. This makes it easier to track your own progress and avoid duplication. When reviewing someone else’s code, you might like to keep a checklist nearby for reference. We humbly suggest that one good list might be the table of contents of the book you are now reading. Enjoy! In summary: We know we’re preaching to the choir, but it had to be said. Your ego may hate a code review, but the little genius programmer inside of you loves it because it gets results and leads to better code and stronger applications.

References [Constantine95] §10, §22, §33 • [McConnell93] §24 • [MozillaCRFAQ]

This page intentionally left blank

Design Style Fools ignore complexity. Pragmatists suffer it. Some can avoid it. Geniuses remove it. —Alan Perlis But I also knew, and forgot, Hoare’s dictum that premature optimization is the root of all evil in programming. —Donald Knuth, The Errors of TeX [Knuth89] It’s difficult to fully separate Design Style and Coding Style. We have tried to leave to the next section those Items that generally crop up when actually writing code. This section focuses on principles and practices that apply more broadly than just to a particular class or function. A classic case in point is the balance among simplicity and clarity (Item 6), avoiding premature optimization (Item 8), and avoiding premature pessimization (Item 9). Those three Items apply, not just at the function-coding level, but to the larger areas of class and module design tradeoffs and to far-reaching application architecture decisions. (They also apply to all programmers. If you think otherwise, please reread the above Knuth quote and note its citation.) Following that, many of the other Items in this and the following section deal with aspects of dependency management—a cornerstone of software engineering and a recurring theme throughout the book. Stop and think of some random good software engineering technique—any good technique. Whichever one you picked, in one way or another it will be about reducing dependencies. Inheritance? Make code written to use the base class less dependent on the actual derived class. Minimize global variables? Reduce long-distance dependencies through widely visible data. Abstraction? Eliminate dependencies between code that manipulates concepts and code that implements them. Information hiding? Make client code less dependent on an entity’s implementation details. An appropriate concern for dependency management is reflected in avoiding shared state (Item 10), applying information hiding (Item 11), and much more. Our vote for the most valuable Item in this section goes to Item 6: Correctness, simplicity, and clarity come first. That they really, really must.

11

12

C++ Coding Standards

5.

Give one entity one cohesive responsibility. Summary Focus on one thing at a time: Prefer to give each entity (variable, class, function, namespace, module, library) one well-defined responsibility. As an entity grows, its scope of responsibility naturally increases, but its responsibility should not diverge.

Discussion A good business idea, they say, can be explained in one sentence. Similarly, each program entity should have one clear purpose. An entity with several disparate purposes is generally disproportionately harder to use, because it carries more than the sum of the intellectual overhead, complexity, and bugs of its parts. Such an entity is larger (often without good reason) and harder to use and reuse. Also, such an entity often offers crippled interfaces for any of its specific purposes because the partial overlap among various areas of functionality blurs the vision needed for crisply implementing each. Entities with disparate responsibilities are typically hard to design and implement. “Multiple responsibilities” frequently implies “multiple personalities”—a combinatorial number of possible behaviors and states. Prefer brief single-purpose functions (see also Item 39), small single-purpose classes, and cohesive modules with clean boundaries. Prefer to build higher-level abstractions from smaller lower-level abstractions. Avoid collecting several low-level abstractions into a larger low-level conglomerate. Implementing a complex behavior out of several simple ones is easier than the reverse.

Examples Example 1: realloc. In Standard C, realloc is an infamous example of bad design. It has to do too many things: allocate memory if passed NULL, free it if passed a zero size, reallocate it in place if it can, or move memory around if it cannot. It is not easily extensible. It is widely viewed as a shortsighted design failure. Example 2: basic_string. In Standard C++, std::basic_string is an equally infamous example of monolithic class design. Too many “nice-to-have” features were added to a bloated class that tries to be a container but isn’t quite, is undecided on iteration vs. indexing, and gratuitously duplicates many standard algorithms while leaving little space for extensibility. (See Item 44‘s Example.)

References [Henney02a] • [Henney02b] • [McConnell93] §10.5 • [Stroustrup00] §3.8, §4.9.4, §23.4.3.1 • [Sutter00] §10, §12, §19, §23 • [Sutter02] §1 • [Sutter04] §37-40

Design Style

6.

13

Correctness, simplicity, and clarity come first. Summary KISS (Keep It Simple Software): Correct is better than fast. Simple is better than complex. Clear is better than cute. Safe is better than insecure (see Items 83 and 99).

Discussion It’s hard to overstate the value of simple designs and clear code. Your code’s maintainer will thank you for making it understandable—and often that will be your future self, trying to remember what you were thinking six months ago. Hence such classic wisdom as: Programs must be written for people to read, and only incidentally for machines to execute. —Harold Abelson and Gerald Jay Sussman Write programs for people first, computers second. —Steve McConnell The cheapest, fastest and most reliable components of a computer system are those that aren’t there. —Gordon Bell Those missing components are also the most accurate (they never make mistakes), the most secure (they can’t be broken into), and the easiest to design, document, test and maintain. The importance of a simple design can’t be overemphasized. —Jon Bentley Many of the Items in this book naturally lead to designs and code that are easy to change, and clarity is the most desirable quality of easy-to-maintain, easy-to-refactor programs. What you can’t comprehend, you can’t change with confidence. Probably the most common tension in this area is between code clarity and code optimization (see Items 7, 8, and 9). When—not if—you face the temptation to optimize prematurely for performance and thereby pessimize clarity, recall Item 8‘s point: It is far, far easier to make a correct program fast than it is to make a fast program correct. Avoid the language’s “dusty corners.” Use the simplest techniques that are effective.

Examples Example 1: Avoid gratuitous/clever operator overloading. One needlessly weird GUI library had users write w + c; to add a child control c to a widget w. (See Item 26.) Example 2: Prefer using named variables, not temporaries, as constructor parameters. This avoids possible declaration ambiguities. It also often makes the purpose of your code clearer and thus is easier to maintain. It’s also often safer (see Items 13 and 31).

References [Abelson96] • [Bentley00] §4 • [Cargill92] pp. 91-93 • [Cline99] §3.05-06 • [Constantine95] §29 • [Keffer95] p. 17 • [Lakos96] §9.1, §10.2.4 • [McConnell93] • [Meyers01] §47 • [Stroustrup00] §1.7, §2.1, §6.2.3, §23.4.2, §23.4.3.2 • [Sutter00] §40-41, §46 • [Sutter04] §29

14

C++ Coding Standards

7.

Know when and how to code for scalability. Summary Beware of explosive data growth: Without optimizing prematurely, keep an eye on asymptotic complexity. Algorithms that work on user data should take a predictable, and preferably no worse than linear, time with the amount of data processed. When optimization is provably necessary and important, and especially if it’s because data volumes are growing, focus on improving big-Oh complexity rather than on micro-optimizations like saving that one extra addition.

Discussion This Item illustrates one significant balance point between Items 8 and 9, “don’t optimize prematurely” and “don’t pessimize prematurely.” That makes this a tough Item to write, lest it be misconstrued as “premature optimization.” It is not that. Here’s the background and motivation: Memory and disk capacity continue to grow exponentially; for example, from 1988 to 2004 disk capacity grew by about 112% per year (nearly 1,900-fold growth per decade), whereas even Moore’s Law is just 59% per year (100-fold per decade). One clear consequence is that whatever your code does today it may be asked to do tomorrow against more data—much more data. A bad (worse than linear) asymptotic behavior of an algorithm will sooner or later bring the most powerful system to its knees: Just throw enough data at it. Defending against that likely future means we want to avoid “designing in” what will become performance pits in the face of larger files, larger databases, more pixels, more windows, more processes, more bits sent over the wire. One of the big success factors in future-proofing of the C++ standard library has been its performance complexity guarantees for the STL container operations and algorithms. Here’s the balance: It would clearly be wrong to optimize prematurely by using a less clear algorithm in anticipation of large data volumes that may never materialize. But it would equally clearly be wrong to pessimize prematurely by turning a blind eye to algorithmic complexity, a.k.a. “big-Oh” complexity, namely the cost of the computation as a function of the number of elements of data being worked on. There are two parts to this advice. First, even before knowing whether data volumes will be large enough to be an issue for a particular computation, by default avoid using algorithms that work on user data (which could grow) but that don’t scale well with data unless there is a clear clarity and readability benefit to using a less scalable algorithm (see Item 6). All too often we get surprised: We write ten pieces of code thinking they’ll never have to operate on huge data sets, and then we’ll turn out to be perfectly right nine of the ten times. The tenth time, we’ll fall into a performance

Design Style

15

pit—we know it has happened to us, and we know it has happened or will happen to you. Sure, we go fix it and ship the fix to the customer, but it would be better to avoid such embarrassment and rework. So, all things being equal (including clarity and readability), do the following up front: x Use flexible, dynamically-allocated data instead of fixed-size arrays: Arrays “larger than the largest I’ll ever need” are a terrible correctness and security fallacy. (See Item 77.) Arrays are acceptable when sizes really are fixed at compile time. x Know your algorithm’s actual complexity: Beware subtle traps like linear-seeming algorithms that actually call other linear operations, making the algorithm actually quadratic. (See Item 81 for an example.) x Prefer to use linear algorithms or faster wherever possible: Constant-time complexity, such as push_back and hash table lookup, is perfect (see Items 76 and 80). O(log N) logarithmic complexity, such as set/map operations and lower_bound and upper_bound with random-access iterators, is good (see Items 76, 85, and 86). O(N) linear complexity, such as vector::insert and for_each, is acceptable (see Items 76, 81, and 84). x Try to avoid worse-than-linear algorithms where reasonable: For example, by default spend some effort on finding a replacement if you’re facing a O(N log N) or 2 O(N ) algorithm, so that your code won’t fall into a disproportionately deep performance pit in the event that data volumes grow significantly. For example, this is a major reason why Item 81 advises to prefer range member functions (which are generally linear) over repeated calls of their single-element counterparts (which easily becomes quadratic as one linear operation invokes another linear operation; see Example 1 of Item 81). x Never use an exponential algorithm unless your back is against the wall and you really have no other option: Search hard for an alternative before settling for an exponential algorithm, where even a modest increase in data volume means falling off a performance cliff. Second, after measurements show that optimization is necessary and important, and especially if it’s because data volumes are growing, focus on improving big-Oh complexity rather than on micro-optimizations like saving that one extra addition. In sum: Prefer to use linear (or better) algorithms wherever possible. Avoid worsethan-linear polynomial algorithms where reasonable. Avoid exponential algorithms with all your might.

References [Bentley00] §6, §8, Appendix 4 • [Cormen01] • [Kernighan99] §7 • [Knuth97a] • [Knuth97b] • [Knuth98] • [McConnell93] §5.1-4, §10.6 • [Murray93] §9.11 • [Sedgewick98] • [Stroustrup00] §17.1.2

16

C++ Coding Standards

8.

Don’t optimize prematurely. Summary Spur not a willing horse (Latin proverb): Premature optimization is as addictive as it is unproductive. The first rule of optimization is: Don’t do it. The second rule of optimization (for experts only) is: Don’t do it yet. Measure twice, optimize once.

Discussion As [Stroustrup00] §6’s introduction quotes so deliciously: Premature optimization is the root of all evil. —Donald Knuth [quoting Hoare] On the other hand, we cannot ignore efficiency. —Jon Bentley Hoare and Knuth are, of course and as always, completely correct (see Item 6 and this Item). So is Bentley (see Item 9). We define premature optimization as making designs or code more complex, and so less readable, in the name of performance when the effort is not justified by a proven performance need (such as actual measurement and comparison against goals) and thus by definition adds no proven value to your program. All too often, unneeded and unmeasured optimization efforts don’t even make the program any faster. Always remember: It is far, far easier to make a correct program fast than it is to make a fast program correct. So, by default, don’t focus on making code fast; focus first on making code as clear and readable as possible (see Item 6). Clear code is easier to write correctly, easier to understand, easier to refactor—and easier to optimize. Complications, including optimizations, can always be introduced later—and only if necessary. There are two major reasons why premature optimizations frequently don’t even make the program faster. First, we programmers are notoriously bad at estimating what code will be faster or smaller, and where the bottlenecks in our code will be. This includes the authors of this book, and it includes you. Consider: Modern computers feature an extremely complex computational model, often with several pipelined processing units working in parallel, a deep cache hierarchy, speculative execution, branch prediction… and that’s just the CPU chip. On top of the hardware, compilers take their best guess at transforming your source code into machine code that exploits the hardware at its best. And on top of all that complication, it’s… well, it’s your guess. So if you go with nothing but guesswork, there is little chance your ill-targeted micro-optimizations will significantly improve things. So, optimization must be preceded by measurement; and measurement must be preceded by optimi-

Design Style

17

zation goals. Until the need is proven, your focus should be on priority #1—writing code for humans. (When someone asks you to optimize, do demand proof.) Second, in modern programs, increasingly many operations aren’t CPU-bound anyway. They may be memory-bound, network-bound, disk-bound, waiting on a web service, or waiting on a database. At best, tuning application code in such operations only make the operations wait faster. It also means that the programmer wasted valuable time improving what didn’t need improving instead of adding value by improving what did. Of course, the day will come when you do need to optimize some code. When you do so, look first for an algorithmic optimization (see Item 7) and try to encapsulate and modularize the optimization (e.g., in a function or class; see Items 5 and 11), and clearly state in a comment the reason of the optimization and a reference to the algorithm used. A common beginner’s mistake is to write new code while obsessing—with pride!— over optimal execution at the cost of understandability. More often than not, this yields miles of spaghetti that, even if correct in the beginning, is hard to read and change. (See Item 6.) It is not premature optimization to pass by reference (see Item 25), to prefer calling prefix ++ and -- (see Item 28), and use similar idioms that should just naturally flow out of our fingertips. These are not premature optimizations; they are simply avoiding premature pessimizations (see Item 9).

Examples Example: An inline irony. Here is a simple demonstration of the hidden cost of a premature micro-optimization: Profilers are excellent at telling you, by function hit count, what functions you should have marked inline but didn’t; profilers are terrible at telling you what functions you did mark inline but shouldn’t have. Too many programmers “inline by default” in the name of optimization, nearly always trading higher coupling for at best dubious benefit. (This assumes that writing inline even matters on your compiler. See [Sutter00], [Sutter02], and [Sutter04].)

Exceptions When writing libraries, it’s harder to predict what operations will end up being used in performance-sensitive code. But even library authors run performance tests against a broad range of client code before committing to obfuscating optimizations.

References [Bentley00] §6 • [Cline99] §13.01-09 • [Kernighan99] §7 • [Lakos96] §9.1.14 • [Meyers97] §33 • [Murray93] §9.9-10, §9.13 • [Stroustrup00] §6 introduction • [Sutter00] §30, §46 • [Sutter02] §12 • [Sutter04] §25

18

C++ Coding Standards

9.

Don’t pessimize prematurely. Summary Easy on yourself, easy on the code: All other things being equal, notably code complexity and readability, certain efficient design patterns and coding idioms should just flow naturally from your fingertips and are no harder to write than the pessimized alternatives. This is not premature optimization; it is avoiding gratuitous pessimization.

Discussion Avoiding premature optimization does not imply gratuitously hurting efficiency. By premature pessimization we mean writing such gratuitous potential inefficiencies as: x Defining pass-by-value parameters when pass-by-reference is appropriate. (See Item 25.) x Using postfix ++ when the prefix version is just as good. (See Item 28.) x Using assignment inside constructors instead of the initializer list. (See Item 48.) It is not a premature optimization to reduce spurious temporary copies of objects, especially in inner loops, when doing so doesn’t impact code complexity. Item 18 encourages variables that are declared as locally as possible, but includes the exception that it can be sometimes beneficial to hoist a variable out of a loop. Most of the time that won’t obfuscate the code’s intent at all, and it can actually help clarify what work is done inside the loop and what calculations are loop-invariant. And of course, prefer to use algorithms instead of explicit loops. (See Item 84.) Two important ways of crafting programs that are simultaneously clear and efficient are to use abstractions (see Items 11 and 36) and libraries (see Item 84). For example, using the standard library’s vector, list, map, find, sort and other facilities, which have been standardized and implemented by world-class experts, not only makes your code clearer and easier to understand, but it often makes it faster to boot. Avoiding premature pessimization becomes particularly important when you are writing a library. You typically can’t know all contexts in which your library will be used, so you will want to strike a balance that leans more toward efficiency and reusability in mind, while at the same time not exaggerating efficiency for the benefit of a small fraction of potential callers. Drawing the line is your task, but as Item 7 shows, the bigger fish to focus on is scalability and not a little cycle-squeezing.

References [Keffer95] pp.12-13 • [Stroustrup00] §6 introduction • [Sutter00] §6

Design Style

19

10. Minimize global and shared data. Summary Sharing causes contention: Avoid shared data, especially global data. Shared data increases coupling, which reduces maintainability and often performance.

Discussion This statement is more general than Item 18‘s specific treatment. Avoid data with external linkage at namespace scope or as static class members. These complicate program logic and cause tighter coupling between different (and, worse, distant) parts of the program. Shared data weakens unit testing because the correctness of a piece of code that uses shared data is conditioned on the history of changes to the data, and further conditions the functioning of acres of yet-unknown code that subsequently uses the data further. Names of objects in the global namespace additionally pollute the global namespace. If you must have global, namespace-scope, or static class objects, be sure to initialize such objects carefully. The order of initialization of such objects in different compilation units is undefined, and special techniques are needed to handle it correctly (see References). The order-of-initialization rules are subtle; prefer to avoid them, but if you do have to use them then know them well and use them with great care. Objects that are at namespace scope, static members, or shared across threads or processes will reduce parallelism in multithreaded and multiprocessor environments and are a frequent source of performance and scalability bottlenecks. (See Item 7.) Strive for “shared-nothing;” prefer communication (e.g., message queues) over data sharing. Prefer low coupling and minimized interactions between classes. (See [Cargill92].)

Exceptions The program-wide facilities cin, cout, and cerr are special and are implemented specially. A factory has to maintain a registry of what function to call to create a given type, and there is typically one registry for the whole program (but preferably it should be internal to the factory rather than a shared global object; see Item 11). Code that does share objects across threads should always serialize all access to those shared objects. (See Item 12 and [Sutter04c].)

References [Cargill92] pp. 126.136, 169-173 • [Dewhurst03] §3 • [Lakos96] §2.3.1 • [McConnell93] §5.1-4 • [Stroustrup00] §C.10.1 • [Sutter00] §47 • [Sutter02] §16, Appendix A • [Sutter04c] • [SuttHysl03]

20

C++ Coding Standards

11. Hide information. Summary Don’t tell: Don’t expose internal information from an entity that provides an abstraction.

Discussion To minimize dependencies between calling code that manipulates an abstraction and the abstraction’s implementation(s), data that is internal to the implementation must be hidden. Otherwise, calling code can access—or, worse, manipulate—that information, and the intended-to-be-internal information has leaked into the abstraction on which calling code depends. Expose an abstraction (preferably a domain abstraction where available, but at least a get/set abstraction) instead of data. Information hiding improves a project’s cost, schedule, and/or risk in two main ways: x It localizes changes: Information hiding reduces the “ripple effect” scope of changes, and therefore their cost. x It strengthens invariants: It limits the code responsible for maintaining (and, if it is buggy, possibly breaking) program invariants. (See Item 41.) Don’t expose data from any entity that provides an abstraction (see also Item 10). Data is just one possible incarnation of abstract, conceptual state. If you focus on concepts and not on their representations you can offer a suggestive interface and tweak implementation at will—such as caching vs. computing on-the-fly or using various representations that optimize certain usage patterns (e.g., polar vs. Cartesian). A common example is to never expose data members of class types by making them public (see Item 41) or by giving out pointers or handles to them (see Item 42), but this applies equally to larger entities such as libraries, which must likewise not expose internal information. Modules and libraries likewise prefer to provide interfaces that define abstractions and traffic in those, and thereby allow communication with calling code to be safer and less tightly coupled than is possible with data sharing.

Exceptions Testing code often needs white-box access to the tested class or module. Value aggregates (“C-style structs”) that simply bundle data without providing any abstraction do not need to hide their data; the data is the interface. (See Item 41.)

References [Brooks95] §19 • [McConnell93] §6.2 • [Parnas02] • [Stroustrup00] §24.4 • [SuttHysl04a]

Design Style

21

12. Know when and how to code for concurrency. Summary Th sa rea fe d ly: If your application uses multiple threads or processes, know how to minimize sharing objects where possible (see Item 10) and share the right ones safely.

Discussion Threading is a huge domain. This Item exists because that domain is important and needs to be explicitly acknowledged, but one Item can’t do it justice and we will only summarize a few essentials; see the References for many more details and techniques. Among the most important issues are to avoid deadlocks, livelocks, and malign race conditions (including corruption due to insufficient locking). The C++ Standard says not one word about threads. Nevertheless, C++ is routinely and widely used to write solid multithreaded code. If your application shares data across threads, do so safely: x Consult your target platforms’ documentation for local synchronization primitives: Typical ones range from lightweight atomic integer operations to memory barriers to in-process and cross-process mutexes. x Prefer to wrap the platform’s primitives in your own abstractions: This is a good idea especially if you need cross-platform portability. Alternatively, you can use a library (e.g., pthreads [Butenhof97]) that does it for you. x Ensure that the types you are using are safe to use in a multithreaded program: In particular, each type must at minimum: x Guarantee that unshared objects are independent: Two threads can freely use different objects without any special action on the caller’s part. x Document what the caller needs to do to use the same object of that type in different threads: Many types will require you to serialize access to such shared objects, but some types do not; the latter typically either design away the locking requirement, or they do the locking internally themselves, in which case, you still need to be aware of the limits of what the internal locking granularity will do. Note that the above applies regardless of whether the type is some kind of string type, or an STL container like a vector, or any other type. (We note that some authors have given advice that implies the standard containers are somehow special. They are not; a container is just another object.) In particular, if you

22

C++ Coding Standards want to use standard library components (e.g., string, containers) in a multithreaded program, consult your standard library implementation’s documentation to see whether that is supported, as described earlier. When authoring your own type that is intended to be usable in a multithreaded program, you must do the same two things: First, you must guarantee that different threads can use different objects of that type without locking (note: a type with modifiable static data typically can’t guarantee this). Second, you must document what users need to do in order to safely use the same object in different threads; the fundamental design issue is how to distribute the responsibility of correct execution (race- and deadlock-free) between the class and its client. The main options are: x External locking: Callers are responsible for locking. In this option, code that uses an object is responsible for knowing whether the object is shared across threads and, if so, for serializing all uses of the object. For example, string types typically use external locking (or immutability; see the third option on the next page). x Internal locking: Each object serializes all access to itself, typically by locking every public member function, so that callers may not need to serialize uses of the object. For example, producer/consumer queues typically use internal locking, because their whole raison d’être is to be shared across threads, and their interfaces are designed so that the appropriate level of locking is for the duration of individual member function calls (Push, Pop). More generally, note that this option is appropriate only when you know two things: First, you must know up front that objects of the type will nearly always be shared across threads, otherwise you’ll end up doing needless locking. Note that most types don’t meet this condition; the vast majority of objects even in a heavily multithreaded program are never shared across threads (and this is good; see Item 10). Second, you must know up front that per-member-function locking is at the right granularity and will be sufficient for most callers. In particular, the type’s interface should be designed in favor of coarse-grained, self-sufficient operations. If the caller typically needs to lock several operations, rather than an operation, this is inappropriate; individually locked functions can only be assembled into a larger-scale locked unit of work by adding more (external) locking. For example, consider a container type that returns an iterator that could become invalid before you could use it, or provides a member algorithm like find that can return a correct answer that could become the wrong answer before you could use it, or has users who want to write if( c.empty() ) c.push_back(x);. (See [Sutter02] for additional examples.) In such cases, the caller needs to perform external locking anyway in order to get a lock whose lifetime spans multiple individual member function calls, and so internal locking of each member function is needlessly wasteful.

Design Style

23

So, internal locking is tied to the type’s public interface: Internal locking becomes appropriate when the type’s individual operations are complete in themselves; in other words, the type’s level of abstraction is raised and expressed and encapsulated more precisely (e.g., as a producer-consumer queue rather than a plain vector). Combining primitive operations together to form coarser common operations is the approach needed to ensure meaningful but simple function calls. Where combinations of primitives can be arbitrary and you cannot capture the reasonable set of usage scenarios in one named operation, there are two alternatives: a) use a callback-based model (i.e., have the caller call a single member function, but pass in the task they want performed as a command or function object; see Items 87 to 89); or b) expose locking in the interface in some way. x Lock-free designs, including immutability (read-only objects): No locking needed. It is possible to design types so that no locking at all is needed (see References). One common example is immutable objects, which do not need to be locked because they never change; for example, for an immutable string type, a string object is never modified once created, and every string operation results in the creation of a new string. Note that calling code should not need to know about your types’ implementation details (see Item 11). If your type uses under-the-covers data-sharing techniques (e.g., copy-on-write), you do not need to take responsibility for all possible thread safety issues, but you must take responsibility for restoring “just enough” thread safety to guarantee that calling code will be correct if it performs its usual duty of care: The type must be as safe to use as it would be if it didn’t use covert implementation-sharing. (See [Sutter04c].) As noted, all properly written types must allow manipulation of distinct visible objects in different threads without synchronization. Particularly if you are authoring a widely-used library, consider making your objects safe to use in a multithreaded program as described above, but without added overhead in a single-threaded program. For example, if you are writing a library containing a type that uses copy-on-write, and must therefore do at least some internal locking, prefer to arrange for the locking to disappear in single-threaded builds of your library (#ifdefs and no-op implementations are common strategies). When acquiring multiple locks, avoid deadlock situations by arranging for all code that acquires the same locks to acquire them in the same order. (Releasing the locks can be done in any order.) One solution is to acquire locks in increasing order by memory address; addresses provide a handy, unique, application-wide ordering.

References [Alexandrescu02a] • [Alexandrescu04] • [Butenhof97] • [Henney00] • [Henney01] • [Meyers04] • [Schmidt01] • [Stroustrup00] §14.9 • [Sutter02] §16 • [Sutter04c]

24

C++ Coding Standards

13. Ensure resources are owned by objects. Use explicit RAII and smart pointers. Summary Don’t saw by hand when you have power tools: C++’s “resource acquisition is initialization” (RAII) idiom is the power tool for correct resource handling. RAII allows the compiler to provide strong and automated guarantees that in other languages require fragile hand-coded idioms. When allocating a raw resource, immediately pass it to an owning object. Never allocate more than one resource in a single statement.

Discussion C++’s language-enforced constructor/destructor symmetry mirrors the symmetry inherent in resource acquire/release function pairs such as fopen/fclose, lock/unlock, and new/delete. This makes a stack-based (or reference-counted) object with a resource-acquiring constructor and a resource-releasing destructor an excellent tool for automating resource management and cleanup. The automation is easy to implement, elegant, low-cost, and inherently error-safe. If you choose not to use it, you are choosing the nontrivial and attention-intensive task of pairing the calls correctly by hand, including in the presence of branched control flows and exceptions. Such C-style reliance on micromanaging resource deallocation is unacceptable when C++ provides direct automation via easy-to-use RAII. Whenever you deal with a resource that needs paired acquire/release function calls, encapsulate that resource in an object that enforces pairing for you and performs the resource release in its destructor. For example, instead of calling a pair of OpenPort/ClosePort nonmember functions directly, consider: class Port { public: Port( const string& destination ); // call OpenPort ~Port(); // call ClosePort // … ports can’t usually be cloned, so disable copying and assignment … }; void DoSomething() { Port port1( “server1:80” ); // … } // can’t forget to close port1; it’s closed automatically at the end of the scope shared_ptr port2 = /*…*/;

// port2 is closed automatically when the // last shared_ptr referring to it goes away

You can also use libraries that implement the pattern for you (see [Alexandrescu00c]).

Design Style

25

When implementing RAII, be conscious of copy construction and assignment (see Item 49); the compiler-generated versions probably won’t be correct. If copying doesn’t make sense, explicitly disable both by making them private and not defined (see Item 53). Otherwise, have the copy constructor duplicate the resource or reference-count the number of uses, and have the assignment operator do the same and ensure that it frees its originally held resource if necessary. A classic oversight is to free the old resource before the new resource is successfully duplicated (see Item 71). Make sure that all resources are owned by objects. Prefer to hold dynamically allocated resources via smart pointers instead of raw pointers. Also, perform every explicit resource allocation (e.g., new) in its own statement that immediately gives the allocated resource to a manager object (e.g., shared_ptr); otherwise, you can leak resources because the order of evaluation of a function’s parameters is undefined. (See Item 31.) For example: void Fun( shared_ptr sp1, shared_ptr sp2 ); // … Fun( shared_ptr(new Widget), shared_ptr(new Widget) ); Such code is unsafe. The C++ Standard gives compilers great leeway to reorder the two expressions building the function’s two arguments. In particular, the compiler can interleave execution of the two expressions: Memory allocation (by calling operator new) could be done first for both objects, followed by attempts to call the two Widget constructors. That very nicely sets things up for a leak because if one of the constructor calls throws an exception, then the other object’s memory will never be released! (See [Sutter02] for details.) This subtle problem has a simple solution: Follow the advice to never allocate more than one resource in a single statement, and perform every explicit resource allocation (e.g., new) in its own code statement that immediately gives the resource to an owning object (e.g., shared_ptr). For example: shared_ptr sp1(new Widget), sp2(new Widget); Fun( sp1, sp2 ); See also Item 31 for other advantages to using this style.

Exceptions Smart pointers can be overused. Raw pointers are fine in code where the pointed-to object is visible to only a restricted quantity of code (e.g., purely internal to a class, such as a Tree class’s internal node navigation pointers).

References [Alexandrescu00c] • [Cline99] §31.03-05 • [Dewhurst03] §24, §67 • [Meyers96] §9-10 • [Milewski01] • [Stroustrup00] §14.3-4, §25.7, §E.3, §E.6 • [Sutter00] §16 • [Sutter02] §20-21 • [Vandevoorde03] §20.1.4

This page intentionally left blank

Coding Style One man’s constant is another man’s variable. —Alan Perlis

In this section, we tighten our focus from general design issues to issues that arise most often during actual coding. The rules and guidelines in this section target coding practices that aren’t specific to a particular language area (e.g., functions, classes, or namespaces) but that improve the quality of your code. Many of these idioms are about getting your compiler to help you, including the powerful tool of declarative const (Item 15) and internal #include guards (Item 24). Others will help you steer clear of land mines (including some outright undefined behavior) that your compiler can’t always check for you, including avoiding macros (Item 16) and uninitialized variables (Item 19). All of them help to make your code more reliable. Our vote for the most valuable Item in this section goes to Item 14: Prefer compileand link-time errors to run-time errors.

27

28

C++ Coding Standards

14. Prefer compile- and link-time errors to run-time errors. Summary Don’t put off ’til run time what you can do at build time: Prefer to write code that uses the compiler to check for invariants during compilation, instead of checking them at run time. Run-time checks are control- and data-dependent, which means you’ll seldom know whether they are exhaustive. In contrast, compile-time checking is not control- or data-dependent and typically offers higher degrees of confidence.

Discussion The C++ language offers many opportunities to “accelerate” error detection by pushing it to compilation time. Exploiting these static checking capabilities offers you many advantages, including the following: x Static checks are data- and flow-independent: Static checking offers guarantees that are independent of the program inputs or execution flow. In contrast, to make sure that your run-time checking is strong enough, you need to test it for a representative sample of all inputs. This is a daunting task for all but the most trivial systems. x Statically expressed models are stronger: Oftentimes, a program that relies less on run-time checks and more on compile-time checks reflects a better design because the model the program creates is properly expressed using C++’s type system. This way, you and the compiler are partners having a consistent view of the program’s invariants; run-time checks are often a fallback to do checking that could be done statically but cannot be expressed precisely in the language. (See Item 68.) x Static checks don’t incur run-time overhead: With static checks replacing dynamic checks, the resulting executable will be faster without sacrificing correctness. One of C++’s most powerful static checking tools is its static type checking. The debate on whether types should be checked statically (C++, Java, ML, Haskell) or dynamically (Smalltalk, Ruby, Python, Lisp) is open and lively. There is no clear winner in the general case, and there are languages and development styles that favor either kind of checking with reportedly good results. The static checking crowd argues that a large category of run-time error handling can be thus easily eliminated, resulting in stronger programs. On the other hand, the dynamic checking camp says that com-

Coding Style

29

pilers can only check a fraction of potential bugs, so if you need to write unit tests anyway you might as well not bother with static checking at all and get a less restrictive programming environment. One thing is clear: Within the context of the statically typed language C++, which provides strong static checking and little automatic run-time checking, programmers should definitely use the type system to their advantage wherever possible (see also Items 90 through 100). At the same time, run-time checks are sensible for data- and flow-dependent checking (e.g., array bounds checking or input data validation) (see Items 70 and 71).

Examples There are several instances in which you can replace run-time checks with compiletime checks. Example 1: Compile-time Boolean conditions. If you are testing for compile-time Boolean conditions such as sizeof(int) >= 8, use static assertions instead of run-time tests. (But see also Item 91.) Example 2: Compile-time polymorphism. Consider replacing run-time polymorphism (virtual functions) with compile-time polymorphism (templates) when defining generic functions or types. The latter yields code that is better checked statically. (See also Item 64.) Example 3: Enums. Consider defining enums (or, better yet, full-fledged types) when you need to express symbolic constants or restricted integral values. Example 4: Downcasting. If you frequently use dynamic_cast (or, worse, an unchecked static_cast) to perform downcasting, it can be a sign that your base classes offer too little functionality. Consider redesigning your interfaces so that your program can express computation in terms of the base class.

Exceptions Some conditions cannot be checked at compile time and require run-time checks. For these, prefer to use assertions to detect internal programming errors (see Item 68) and follow the advice in the rest of the error handling section for other run-time errors such as data-dependent errors (see Items 69 through 75).

References [Alexandrescu01] §3 • [Boost] • [Meyers97] §46 • [Stroustrup00] §2.4.2 • [Sutter02] §4 • [Sutter04] §2, §19

30

C++ Coding Standards

15. Use const proactively. Summary const is your friend: Immutable values are easier to understand, track, and reason about, so prefer constants over variables wherever it is sensible and make const your default choice when you define a value: It’s safe, it’s checked at compile time (see Item 14), and it’s integrated with C++’s type system. Don’t cast away const except to call a const-incorrect function (see Item 94).

Discussion Constants simplify code because you only have to look at where the constant is defined to know its value everywhere. Consider this code: void Fun( vector& v ) { // … const size_t len = v.size(); // … 30 more lines … } When seeing len’s definition above, you gain instant confidence about len’s semantics throughout its scope (assuming the code doesn’t cast away const, which it should not do; see below): It’s a snapshot of v’s length at a specific point. Just by looking up one line of code, you know len’s semantics over its whole scope. Without the const, len might be later modified, either directly or through an alias. Best of all, the compiler will help you ensure that this truth remains true. Note that const is not deep. For example, consider a class C that has a member of type X*. In C objects that are const, the X* member is also const—but the X object that is pointed to is not. (See [Saks99].) Implement logical constness with mutable members. When a const member function of a class legitimately needs to modify a member variable (i.e., when the variable does not affect the object’s observable state, such as cached data), declare that member variable mutable. Note that if all private members are hidden using the Pimpl idiom (see Item 43), mutable is not needed on either the cached information or the unchanging pointer to it. Yes, const is “viral”—add it in one place, and it wants to propagate throughout your code as you call other functions whose signatures aren’t yet const-correct. This is a

Coding Style

31

feature, not a bug, and this quality greatly increases const’s power even though it was unjustly demeaned in the days when const wasn’t well understood and appreciated. Retrofitting an existing code base to make it const-correct takes effort, but it is worthwhile and likely to uncover latent bugs. Const-correctness is worthwhile, proven, effective, and highly recommended. Understanding how and where a program’s state changes is vital, and const documents that directly in code where the compiler can help to enforce it. Writing const appropriately helps you gain a better understanding of your design and makes your code sturdier and safer. If you find it impossible to make a member function const, you usually gain a better understanding of the ways in which that member function might modify an object’s state. You might also understand which data members bridge the gap between physical constness and logical constness, as noted in the following Examples. Never cast away const except to call a const-incorrect function, or in rare cases as a workaround for lack of mutable on older compilers.

Examples Example: Avoid const pass-by-value function parameters in function declarations. The following two declarations are exactly equivalent: void Fun( int x ); void Fun( const int x );

// redeclares the same function: top-level const is ignored

In the second declaration, the const is redundant. We recommend declaring functions without such top-level consts, so that readers of your header files won’t get confused. However, the top-level const does make a difference in a function’s definition and can be sensible there to catch unintended changes to the parameter: void Fun( const int x ) { // … ++x;

// Fun’s actual definition // error: cannot modify a const value

// … }

References [Allison98] §10 • [Cline99] §14.02-12 • [Dewhurst03] §6, §31-32, §82 • [Keffer95] pp. 5-6 • [Koenig97] §4 • [Lakos96] §9.1.6, §9.1.12 • [Meyers97] §21 • [Murray93] §2.7 • [Stroustrup00] §7.2, §10.2.6, §16.3.1 • [Sutter00] §43

32

C++ Coding Standards

16. Avoid macros. Summary TO_PUT_IT_BLUNTLY: Macros are the bluntest instrument of C and C++’s abstraction facilities, ravenous wolves in functions’ clothing, hard to tame, marching to their own beat all over your scopes. Avoid them.

Discussion It’s hard to find language that’s colorful enough to describe macros, but we’ll try. To quote from [Sutter04] §31: Macros are obnoxious, smelly, sheet-hogging bedfellows for several reasons, most of which are related to the fact that they are a glorified text-substitution facility whose effects are applied during preprocessing, before any C++ syntax and semantic rules can even begin to apply. Lest there remain any ambiguity on this point, we note also that Bjarne Stroustrup has written: I dislike most forms of preprocessors and macros. One of C++’s aims is to make C’s preprocessor redundant (§4.4, §18) because I consider its actions inherently error prone. —[Stroustrup94] §3.3.1 Macros are almost never necessary in C++. Use const (§5.4) or enum (§4.8) to define manifest constants [see Item 15], inline (§7.1.1) to avoid function-calling overhead [but see Item 8], templates (Chapter 13) to specify families of functions and types [see Items 64 through 67], and namespaces (§8.2) to avoid name clashes [see Items 57 through 59]. —[Stroustrup00] §1.6.1 The first rule about macros is: Don’t use them unless you have to. Almost every macro demonstrates a flaw in the programming language, in the program, or in the programmer. —[Stroustrup00] §7.8 The main problem with C++ macros is that they seem much better at what they do than they really are. Macros ignore scopes, ignore the type system, ignore all other language features and rules, and hijack the symbols they #define for the remainder of a file. Macro invocations look like symbols or function calls, but are neither. Macros are not “hygienic,” meaning that they can expand to significantly and surprisingly different things depending on the context in which they are used. The text substitution that macros perform makes writing even remotely proper macros a black art whose mastery is as unrewarding as it is tedious.

Coding Style

33

People who think that template-related errors are the worst to decipher probably haven’t seen those caused by badly formed or badly used macros. Templates are part of C++’s type system and thus allow compilers to get better at handling them (which they do), whereas macros are forever divorced from the language and hence intractable. Worse, unlike a template, a macro might expand to some transmission line noise that undesirably compiles by pure chance. Finally, an error in a macro can only be reported after the macro is expanded and not when it is defined. Even in the rare cases where you do legitimately write a macro (see Exceptions), never ever even consider starting to think about writing a macro that is a common word or abbreviation. Do #undefine macros as soon as possible, always give them SCREAMING_UPPERCASE_AND_UGLY names, and avoid putting them in headers.

Examples Example: Passing a template instantiation to a macro. Macros barely understand C’s parentheses and square brackets well enough to balance them. C++, however, defines a new parenthetical construct, namely the < and > used in templates. Macros can’t pair those correctly, which means that in a macro invocation MACRO( Foo ) the macro thinks it is being passed two arguments, namely Foo, when in fact the construct is one C++ entity.

Exceptions Macros remain the only solution for a few important tasks, such as #include guards (see Item 24), #ifdef and #if defined for conditional compilation, and implementing assert (see Item 68). For conditional compilation (e.g., system-dependent parts), avoid littering your code with #ifdefs. Instead, prefer to organize code such that the use of macros drives alternative implementations of one common interface, and then use the interface throughout. You may want to use macros (cautiously) when the alternative is extreme copying and pasting snippets of code around. We note that both [C99] and [Boost] include moderate and radical extensions, respectively, to the preprocessor.

References [Boost] • [C99] • [Dewhurst03] §25-28 • [Meyers96] §1 • [Lakos96] §2.3.4 • [Stroustrup94] §3.3.1 • [Stroustrup00] §1.6.1, §7.8 • [Sutter02] §34-35 • [Sutter04] §31 • [Sutter04a]

34

C++ Coding Standards

17. Avoid magic numbers. Summary Programming isn’t magic, so don’t incant it: Avoid spelling literal constants like 42 or 3.14159 in code. They are not self-explanatory and complicate maintenance by adding a hard-to-detect form of duplication. Use symbolic names and expressions instead, such as width * aspectRatio.

Discussion Names add information and introduce a single point of maintenance; raw numbers duplicated throughout a program are anonymous and a maintenance hassle. Constants should be enumerators or const values, scoped and named appropriately. One 42 may not be the same as another 42. Worse, “in-head” computations made by the programmer (e.g., “this 84 comes from doubling the 42 used five lines ago”) make it tedious and error-prone to later replace 42 with another constant. Prefer replacing hardcoded strings with symbolic constants. Keeping strings separate from the code (e.g., in a dedicated .cpp or resource file) lets non-programmers review and update them, reduces duplication, and helps internationalization.

Examples Example 1: Important domain-specific constants at namespace level. const size_t PAGE_SIZE = 8192, WORDS_PER_PAGE = PAGE_SIZE / sizeof(int), INFO_BITS_PER_PAGE = 32 * CHAR_BIT; Example 2: Class-specific constants. You can define static integral constants in the class definition; constants of other types need a separate definition or a short function. // File widget.h class Widget { static const int defaultWidth = 400; // value provided in declaration static const double defaultPercent; // value provided in definition static const char* Name() { return “Widget”; } }; // File widget.cpp const double Widget::defaultPercent = 66.67; // value provided in definition const int Widget::defaultWidth; // definition required

References [Dewhurst03] §2 • [Kernighan99] §1.5 • [Stroustrup00] §4.8, §5.4

Coding Style

35

18. Declare variables as locally as possible. Summary Avoid scope bloat, as with requirements so too with variables: Variables introduce state, and you should have to deal with as little state as possible, with lifetimes as short as possible. This is a specific case of Item 10 that deserves its own treatment.

Discussion Variables whose lifetimes are longer than necessary have several drawbacks: x They make the program harder to understand and maintain: For example, should code update the module-wide path string if it only changes the current drive? x They pollute their context with their name: As a direct consequence of this, namespace-level variables, which are the most visible of all, are also the worst (see Item 10). x They can’t always be sensibly initialized: Never declare a variable before you can initialize it sensibly. Uninitialized variables are a pervasive source of bugs in all C and C++ programs, and they require our proactive attention because they can’t always be detected by compilers (see Item 19). In particular, older versions of C before [C99] required variables to be defined only at the beginning of a scope; this style is obsolete in C++. A serious problem with this restriction is that at the beginning of the scope you often don’t yet have enough information to initialize variables with pertinent information. This leaves you with two choices—either initialize with some default blank value (e.g., zero), which is usually wasteful and can lead to errors if the variable ends up being used before it has a useful state, or leave them uninitialized, which is dangerous. An uninitialized variable of user-defined types will self-initialize to some blank value. The cure is simple: Define each variable as locally as you can, which is usually exactly the point where you also have enough data to initialize it and immediately before its first use.

Exceptions It can sometimes be beneficial to hoist a variable out of a loop. (See Item 9.) Because constants don’t add state, this Item does not apply to them. (See Item 17.)

References [Dewhurst03] §3, §48, §66 • [Dewhurst03] §95 [McConnell93] §5.1-4, §10.1 • [Stroustrup00] §4.9.4, §6.3

36

C++ Coding Standards

19. Always initialize variables. Summary Start with a clean slate: Uninitialized variables are a common source of bugs in C and C++ programs. Avoid such bugs by being disciplined about cleaning memory before you use it; initialize variables upon definition.

Discussion In the low-level efficiency tradition of C and C++ alike, the compiler is often not required to initialize variables unless you do it explicitly (e.g., local variables, forgotten members omitted from constructor initializer lists). Do it explicitly. There are few reasons to ever leave a variable uninitialized. None is serious enough to justify the hazard of undefined behavior. If you’ve used a procedural language (e.g., Pascal, C, Fortran, or Cobol) you might be used to defining variables in separation from the code that uses them, and then assigning them values later when they’re about to be used. This approach is obsolete and not recommended (see Item 18). A common misconception about uninitialized variables is that they will crash the program, so that those few uninitialized variables lying around here and there will be quickly revealed by simple testing. On the contrary, programs with uninitialized variables can run flawlessly for years if the bits in the memory happen to match the program’s needs. Later, a call from a different context, a recompilation, or some change in another part of the program will cause failures ranging from inexplicable behavior to intermittent crashes.

Examples Example 1: Using a default initial value or ?: to reduce mixing data flow with control flow. // Not recommended: Doesn’t initialize variable int speedupFactor; if( condition ) speedupFactor = 2; else speedupFactor = -1; // Better: Initializes variable int speedupFactor = -1; if( condition ) speedupFactor = 2;

Coding Style

37

// Better: Initializes variable int speedupFactor = condition ? 2 : -1; The better alternatives nicely leave no gap between definition and initialization. Example 2: Replacing a complicated computational flow with a function. Sometimes a value is computed in a way that is best encapsulated in a function (see Item 11): // Not recommended: Doesn’t initialize variable int speedupFactor; if( condition ) { // … code … speedupFactor = someValue; } else { // … code … speedupFactor = someOtherValue; } // Better: Initializes variable int speedupFactor = ComputeSpeedupFactor(); Example 3: Initializing arrays. For large aggregate types such as arrays, proper initialization does not always mean having to really touch all the data. For example, say you use an API that forces you to use fixed arrays of char of size MAX_PATH (but see Items 77 and 78). If you are sure the arrays are always treated as null-terminated C strings, this immediate assignment is good enough: // Acceptable: Create an empty path char path[MAX_PATH]; path[0] = ‘\0’; The following safer initialization fills all the characters in the array with zero: // Better: Create a zero-filled path char path[MAX_PATH] = { ‘\0’ }; Both variants above are recommended, but in general you should prefer safety to unneeded efficiency.

Exceptions Input buffers and volatile data that is directly written by hardware or other processes does not need to be initialized by the program.

References [Dewhurst03] §48 • [Stroustrup00] §4.9.5, §6.3

38

C++ Coding Standards

20. Avoid long functions. Avoid deep nesting. Summary Short is better than long, flat is better than deep: Excessively long functions and nested code blocks are often caused by failing to give one function one cohesive responsibility (see Item 5), and both are usually solved by better refactoring.

Discussion Every function should be a coherent unit of work bearing a suggestive name (see Item 5 and the Discussion in Item 70). When a function instead tries to merge such small conceptual elements inside a long function body, it ends up doing too much. Excessive straight-line function length and excessive block nesting depth (e.g., if, for, while, and try blocks) are twin culprits that make functions more difficult to understand and maintain, and often needlessly so. Each level of nesting adds intellectual overhead when reading code because you need to maintain a mental stack (e.g., enter conditional, enter loop, enter try, enter conditional, …). Have you ever found a closing brace in someone’s code and wondered which of the many fors, whiles, or ifs it matched? Prefer better functional decomposition to help avoid forcing readers to keep as much context in mind at a time. Exercise common sense and reasonableness: Limit the length and depth of your functions. All of the following good advice also helps reduce length and nesting: x x x x

Prefer cohesion: Give one function one responsibility (see Item 5). Don’t repeat yourself: Prefer a named function over repeated similar code snippets. Prefer &&: Avoid nested consecutive ifs where an && condition will do. Don’t try too hard: Prefer automatic cleanup via destructors over try blocks (see Item 13). x Prefer algorithms: They’re flatter than loops, and often better (see Item 84). x Don’t switch on type tags. Prefer polymorphic functions (see Item 90).

Exceptions A function might be legitimately long and/or deep when its functionality can’t be reasonably refactored into independent subtasks because every potential refactoring would require passing many local variables and context (rendering the result less readable rather than more readable). But if several such potential functions take similar arguments, they might be candidates for becoming members of a new class.

References [Piwowarski82] • [Miller56]

Coding Style

39

21. Avoid initialization dependencies across compilation units. Summary Keep (initialization) order: Namespace-level objects in different compilation units should never depend on each other for initialization, because their initialization order is undefined. Doing otherwise causes headaches ranging from mysterious crashes when you make small changes in your project to severe non-portability even to new releases of the same compiler.

Discussion When you define two namespace-level objects in different compilation units, which object’s constructor is called first is not defined. Often (but not always) your tools might happen to initialize them in the order in which the compilation units’ object files are linked, but this assumption is usually not reliable; even when it does hold, you don’t want the correctness of your code to subtly depend on your makefile or project file. (For more on the evils of order dependencies, see also Item 59.) Therefore, inside the initialization code of any namespace-level object, you can’t assume that any other object defined in a different compilation unit has already been initialized. These considerations apply to dynamically initialized variables of primitive types, such as a namespace-level bool reg_success = LibRegister(“mylib”); Note that, even before they are ever constructed using a constructor, namespacelevel objects are statically initialized with all zeroes (as opposed to, say, automatic objects that initially contain garbage). Paradoxically, this zero-initialization can make bugs harder to detect, because instead of crashing your program swiftly the static zero-initialization gives your yet-uninitialized object an appearance of legitimacy. You’d think that that string is empty, that pointer is null, and that integer is zero, when in fact no code of yours has bothered to initialize them yet. To avoid this problem, avoid namespace-level variables wherever possible; they are dangerous (see Item 10). When you do need such a variable that might depend upon another, consider the Singleton design pattern; used carefully, it might avoid implicit dependencies by ensuring that an object is initialized upon first access. Still, Singleton is a global variable in sheep’s clothing (see again Item 10), and is broken by mutual or cyclic dependencies (again, zero-initialization only adds to the confusion).

References [Dewhurst03] §55 • [Gamma95] • [McConnell93] §5.1-4 • [Stroustrup00] §9.4.1, §10.4.9

40

C++ Coding Standards

22. Minimize definitional dependencies. Avoid cyclic dependencies. Summary Don’t be over-dependent: Don’t #include a definition when a forward declaration will do. Don’t be co-dependent: Cyclic dependencies occur when two modules depend directly or indirectly on one another. A module is a cohesive unit of release (see page 103); modules that are interdependent are not really individual modules, but superglued together into what’s really a larger module, a larger unit of release. Thus, cyclic dependencies work against modularity and are a bane of large projects. Avoid them.

Discussion Prefer forward declarations except where you really need a type’s definition. You need a full definition of a class C in two main cases: x When you need to know the size of a C object: For example, when allocating a C on the stack or as a directly-held member of another type. x When you need to name or call a member of C: For example, when calling a member function. In keeping with this book’s charter, we’ll set aside from the start those cyclic dependencies that cause compile-time errors; you’ve already fixed them by following good advice present in the literature and Item 1. Let’s focus on cyclic dependencies that remain in compilable code, see how they trouble your code’s quality, and what steps need be taken to avoid them. In general, dependencies and their cycles should be thought of at module level. A module is a cohesive collection of classes and functions released together (see Item 5 and page 103). In its simplest form, a cyclic dependency has two classes that directly depend upon each other: class Child;

// breaks the dependency cycle

class Parent { // … Child* myChild_; }; class Child { // … Parent* myParent_; };

// possibly in a different header

Coding Style

41

Parent and Child depend upon each other. The code compiles, but we’ve set the stage for a fundamental problem: The two classes are not independent anymore, but have become interdependent. That is not necessarily bad, but it should only occur when both are part of the same module (developed by the same person or team and tested and released as a whole). In contrast, consider: What if Child did not need to store a back link to its Parent object? Then Child could be released as its own separate, smaller module (and maybe under a different name) in total independence from Parent—clearly a more flexible design. Things get only worse when dependency cycles span multiple modules, which are all stuck together with dependency glue to form a single monolithic unit of release. That’s why cycles are the fiercest enemy of modularity. To break cycles, apply the Dependency Inversion Principle documented in [Martin96a] and [Martin00] (see also Item 36): Don’t make high-level modules depend on low-level modules; instead, make both depend on abstractions. If you can define independent abstract classes for either Parent or Child, you’ve broken the cycle. Otherwise, you must commit to making them parts of the same module. A particular form of dependency that certain designs suffer from is transitive dependency on derived classes, which occurs when a base class depends on all of its descendants, direct and indirect. Some implementations of the Visitor design pattern leads to this kind of dependency. Such a dependency is acceptable only for exceptionally stable hierarchies. Otherwise, you may want to change your design; for example, use the Acyclic Visitor pattern [Martin98]. One symptom of excessive interdependencies is incremental builds that have to build large parts of the project in response to local changes. (See Item 2.)

Exceptions Cycles among classes are not necessarily bad—as long as the classes are considered part of the same module, tested together, and released together. Naïve implementations of such patterns as Command and Visitor result in interfaces that are naturally interdependent. These interdependencies can be broken, but doing so requires explicit design.

References [Alexandrescu01] §3 • [Boost] • [Gamma95] • [Lakos96] §0.2.1, §4.6-14, §5 • [Martin96a] • [Martin96b] • [Martin98] §7 • [Martin00] • [McConnell93] §5 • [Meyers97] §46 • [Stroustrup00] §24.3.5 • [Sutter00] §26 • [Sutter02] §37 • [Sutter03]

42

C++ Coding Standards

23. Make header files self-sufficient. Summary Behave responsibly: Ensure that each header you write is compilable standalone, by having it include any headers its contents depend upon.

Discussion If one header file won’t work unless the file that includes it also includes another header, that’s gauche and puts unnecessary burden on that header file’s users. Years ago, some experts advised that headers should not include other headers because of the cost of opening and parsing a guarded header multiple times. Fortunately, this is largely obsolete: Many modern C++ compilers recognize header guards automatically (see Item 24) and don’t even open the same header twice. Some also offer precompiled headers, which help to ensure that often-used, seldomchanged headers will not be parsed often. But don’t include headers that you don’t need; they just create stray dependencies. Consider this technique to help enforce header self-sufficiency: In your build, compile each header in isolation and validate that there are no errors or warnings.

Examples Some subtler issues arise in connection with templates. Example 1: Dependent names. Templates are compiled at the point where they are defined, except that any dependent names or types are not compiled until the point where the template is instantiated. This means that a template class Widget with a std::deque member does not incur a compile-time error even when is not included, as long as nobody instantiates Widget. Given that Widget exists in order to be instantiated, its header clearly should #include . Example 2: Member function templates, and member functions of templates, are instantiated only if used. Suppose that Widget doesn’t have a member of type std::deque, but Widget’s Transmogrify member function uses a deque. Then Widget’s callers can instantiate and use Widget just fine even if no one includes , as long as they don’t use Transmogrify. By default, the Widget header should still #include because it is necessary for at least some callers of Widget. In rare cases where an expensive header is being included for few rarely used functions of a template, consider refactoring those functions as nonmembers supplied in a separate header that does include the expensive one. (See Item 44.)

References [Lakos96] §3.2 • [Stroustrup00] §9.2.3 • [Sutter00] §26-30 • [Vandevoorde03] §9-10

Coding Style

43

24. Always write internal #include guards. Never write external #include guards. Summary Wear head(er) protection: Prevent unintended multiple inclusions by using #include guards with unique names for all of your header files.

Discussion Each header file should be guarded by an internal #include guard to avoid redefinitions in case it is included multiple times. For example, a header file foo.h should follow the general form: #ifndef FOO_H_INCLUDED_ #define FOO_H_INCLUDED_ // … contents of the file … #endif Observe the following rules when defining include guards: x Use a unique guard name: Make sure it is unique at least within your application. We used a popular convention above; the guard name can include the application name, and some tools generate guard names containing random numbers. x Don’t try to be clever: Don’t put any code or comments before and after the guarded portion, and stick to the standard form as shown. Today’s preprocessors can detect include guards, but they might have limited intelligence and expect the guard code to appear exactly at the beginning and end of the header. Avoid using the obsolete external include guards advocated in older books: #ifndef FOO_H_INCLUDED_ #include “foo.h” #define FOO_H_INCLUDED_ #endif

// NOT recommended

External include guards are tedious, are obsolete on today’s compilers, and are fragile with tight coupling because the callers and header must agree on the guard name.

Exceptions In very rare cases, a header file may be intended to be included multiple times.

References [C++03, §2.1] • [Stroustrup00] §9.3.3

This page intentionally left blank

Functions and Operators If you have a procedure with ten parameters, you probably missed some. —Alan Perlis

Functions, including overloaded operators, are the fundamental units of work. As we will see later on in the section on Error Handling and Exceptions (and particularly in Item 70), this has a direct effect on how we reason about the correctness and safety of our code. But first, let’s consider some fundamental mechanics for writing functions, including operators. In particular, we’ll focus on their parameters, their semantics, and their overloading. Our vote for the most valuable Item in this section goes to Item 26: Preserve natural semantics for overloaded operators.

45

46

C++ Coding Standards

25. Take parameters appropriately by value, (smart) pointer, or reference. Summary Parameterize well: Distinguish among input, output, and input/output parameters, and between value and reference parameters. Take them appropriately.

Discussion Choosing well among values, references, and pointers for parameters is good habit that maximizes both safety and efficiency. Although efficiency should not be our primary up-front concern (see Item 8), neither should we write needlessly inefficient code when all other things, including clarity, are equal (see Item 9). Prefer to follow these guidelines for choosing how to take parameters. For inputonly parameters: x Always const-qualify all pointers or references to input-only parameters. x Prefer taking inputs of primitive types (e.g., char, float) and value objects that are cheap to copy (e.g., Point, complex) by value. x Prefer taking inputs of other user-defined types by reference to const. x Consider pass-by-value instead of reference if the function requires a copy of its argument. This is conceptually identical to taking a reference to const plus doing a copy, and it can help compiler to better optimize away temporaries. For output or input/output parameters: x Prefer passing by (smart) pointer if the argument is optional (so callers can pass null as a “not available” or “don’t care” value) or if the function stores a copy of the pointer or otherwise manipulates ownership of the argument. x Prefer passing by reference if the argument is required and the function won’t store a pointer to it or otherwise affect its ownership. This states that the argument is required and makes the caller responsible for providing a valid object. Don’t use C-style varargs (see Item 98).

References [Alexandrescu03a] • [Cline99] §2.10-11, 14.02-12, 32.08 • [Dewhurst03] §57 • [Koenig97] §4 • [Lakos96] §9.1.11-12 • [McConnell93] §5.7 • [Meyers97] §21-22 • [Stroustrup94] §11.4.4 • [Stroustrup00] §5.5, §11.6, §16.3.4 • [Sutter00] §6, §46

Functions and Operators

47

26. Preserve natural semantics for overloaded operators. Summary Programmers hate surprises: Overload operators only for good reason, and preserve natural semantics; if that’s difficult, you might be misusing operator overloading.

Discussion Although anyone would agree (we hope) that one should not implement subtraction in an operator+ implementation, other cases can be subtle. For example, does your Tensor class’s operator* mean the scalar product or the vector product? Does operator+=( Tensor& t, unsigned u ) add u to each of t’s elements, or will it resize t? In such ambiguous or counterintuitive cases, prefer using named functions instead of fostering cryptic code. For value types (but not all types; see Item 32): “When in doubt, do as the ints do.” [Meyers96] Mimicking the behavior of and relationships among operators on builtin types ensures that you don’t surprise anyone. If your semantics of choice are likely to raise eyebrows, maybe operator overloading is not a good idea. Programmers expect operators to come in bundles. If the expression a @ b is well formed for some operator @ you define (possibly after conversions), ask: Can the caller also write b @ a without surprises? Can the caller write a @= b? (See Item 27.) If the operator has an inverse (e.g., + and -, or * and /), are both supported? Named functions are less likely to have such assumed relationships, and therefore should be preferred for clearer code if there can be any doubt about semantics.

Exceptions There are highly specialized libraries (e.g., parser generators and regular expression engines) that define domain-specific conventions for operators that are very different from their C++ meanings (e.g., a regular expression engine might use operator* to express “zero or more”). Prefer instead to find an alternative to unusual operator overloading (e.g., [C++TR104] regular expressions use strings, so that * can be used naturally without overloading operators). If after careful thought you choose to use operators anyway, make sure you define a coherent framework for your conventions and that you don’t step on the toes of any built-in operator.

References [Cline99] §23.02-06 • [C++TR104] §7 • [Dewhurst03] §85-86 • [Koenig97] §4 • [Lakos96] §9.1.1 • [Meyers96] §6 • [Stroustrup00] §11.1 • [Sutter00] §41

48

C++ Coding Standards

27. Prefer the canonical forms of arithmetic and assignment operators. Summary If you a+b, also a+=b: When defining binary arithmetic operators, provide their assignment versions as well, and write to minimize duplication and maximize efficiency.

Discussion In general, for some binary operator @ (be it +, -, *, and so on), you should define its assignment version such that a @= b and a = a @ b have the same meaning (other than that the first form might be more efficient and only evaluates a once). The canonical way of achieving this goal is to define @ in terms of @=, as follows: T& T::operator@=( const T& ) { // … implementation … return *this; } T operator@( const T& lhs, const T& rhs ) { T temp( lhs ); return temp @= rhs; } The two functions work in tandem. The assignment form does the actual work and returns its left-hand parameter. The non-assignment version creates a temporary from lhs, modifies it by invoking the assignment form, and returns it. Note that here operator@ is a nonmember function, so that it will have the desirable property of accepting the same implicit conversions on its left-hand side and righthand side parameters. (See Item 44.) For example, if you define a class String that has an implicit constructor taking a char, making operator+( const String&, const String& ) a nonmember enables both char + String and String + char to work; a member version String::operator+( const String& ) would only accept the latter. An efficiency-minded implementation might choose to define several nonmember overloads of operator@ to avoid proliferation of temporaries resulted through conversions (see Item 29). Where possible, make operator@= a nonmember function as well (see Item 44). In any case, put all nonmember operators in the same namespace as T so that they will be conveniently available to callers as well as to avoid name lookup surprises (see Item 57).

Functions and Operators

49

A variation is to have operator@ accept its first parameter by value. This way, you arrange for the compiler itself to perform the copy for you implicitly, and this can give the compiler more leeway in applying optimizations: T& operator@=( T& lhs, const T& rhs ) { // … implementation … return lhs; } T operator@( T lhs, const T& rhs ) { return lhs @= rhs; }

// lhs taken by value

Another variation is to have operator@ return a const value. This technique has the advantage that it disables nonsensical code such as a + b = c, but it does so at the cost of disabling some potentially useful constructs such as a = (b + c).replace(pos, n, d)—expressive code that, in one shot, concatenates strings b and c, replaces some characters, and assigns the final result to a.

Examples Example: An implementation of += for strings. When concatenating strings, it is useful to know the length in advance so as to allocate memory only once. String& String::operator+=( const String& rhs ) { // … implementation … }

return *this;

String operator+( const String& lhs, const String& rhs ) { String temp; // initially empty temp.Reserve( lhs.size() + rhs.size() ); // allocate enough memory return (temp += lhs) += rhs; // append the strings and return }

Exceptions In some cases (e.g., operator*= on complex numbers), an operator might mutate its left-hand side so significantly that it can be more advantageous to implement operator*= in terms of operator* rather than the reverse.

References [Alexandrescu03a] • [Cline99] §23.06 • [Meyers96] §22 • [Sutter00] §20

50

C++ Coding Standards

28. Prefer the canonical form of ++ and --. Prefer calling the prefix forms. Summary If you ++c, also c++: The increment and decrement operators are tricky because each has pre- and postfix forms, with slightly different semantics. Define operator++ and operator-- such that they mimic the behavior of their built-in counterparts. Prefer to call the prefix versions if you don’t need the original value.

Discussion An ancient joke about C++ was that the language is called C++ and not ++C because the language is improved (incremented), but many people still use it as C (the previous value). Fortunately, the joke is now obsolete, but it’s a helpful illustration for understanding the difference between the two operator forms. For ++ and --, the postfix forms return the original value, whereas the prefix forms return the new value. Prefer to implement the postfix form in terms of the prefix form. The canonical form is: T& T::operator++() { // perform increment return *this; }

T& T::operator--() { // the prefix form: // perform decrement // - do the work return *this; // - always return *this; }

T T::operator++(int) { T old( *this ); ++*this; return old; }

T T::operator--(int) { T old( *this ); --*this; return old; }

// the postfix form: // - remember old value // - call the prefix version // - return the old value

In calling code, prefer using the prefix form unless you actually need the original value returned by the postfix version. The prefix form is semantically equivalent, just as much typing, and often slightly more efficient by creating one less object. This is not premature optimization; it is avoiding premature pessimization (see Item 9).

Exceptions Expression template frameworks preserve the semantics via different means.

References [Cline99] §23.07-08 • [Dewhurst03] §87 • [Meyers96] §6 • [Stroustrup00] §19.3 • [Sutter00] §6, §20

Functions and Operators

51

29. Consider overloading to avoid implicit type conversions. Summary Do not multiply objects beyond necessity (Occam’s Razor): Implicit type conversions provide syntactic convenience (but see Item 40). But when the work of creating temporary objects is unnecessary and optimization is appropriate (see Item 8), you can provide overloaded functions with signatures that match common argument types exactly and won’t cause conversions.

Discussion If you’re in the office and run out of paper, what do you do? Of course, you walk to your trusty photocopier and make several copies of a white sheet of paper. As silly as it sounds, this is often what implicit conversions do: unnecessarily go through the trouble of creating temporaries, just to perform some trivial operation on them and toss them away (see Item 40). A common example is string comparison: class String { // … String( const char* text ); };

// enables implicit conversion

bool operator==( const String&, const String& ); // … somewhere in the code … if( someString == “Hello” ) { … } Having seen the definitions above, the compiler will compile the comparison as if you had written someString == String(“Hello”). This can be quite wasteful, considering that you don’t need to copy the characters just to read them. The solution to this problem is simple: define overloads that avoid the conversion. For example: bool operator==( const String& lhs, const String& rhs ); bool operator==( const String& lhs, const char* rhs ); bool operator==( const char* lhs, const String& rhs );

// #1 // #2 // #3

That looks like a lot of code duplication, but in reality it is only “signature duplication” because all three typically use the same back-end function. You’re unlikely to commit a premature optimization heresy (see Item 8) with such simple overloads, and it’s de bon goût to provide them especially when designing a library when it’s difficult to predict in advance what common types will be in performance-sensitive code.

References [Meyers96] §21 • [Stroustrup00] §11.4, §C.6 • [Sutter00] §6

52

C++ Coding Standards

30. Avoid overloading &&, ||, or , (comma) . Summary Wisdom means knowing when to refrain: The built-in &&, ||, and , (comma) enjoy special treatment from the compiler. If you overload them, they become ordinary functions with very different semantics (you will violate Items 26 and 31), and this is a sure way to introduce subtle bugs and fragilities. Don’t overload these operators naïvely.

Discussion The primary reason not to overload operator&&, operator||, or operator, (comma) is that you cannot implement the full semantics of the built-in operators in these three cases, and programmers commonly expect those semantics. In particular, the built-in versions evaluate left-to-right, and for && and || also use short-circuit evaluation. The built-in versions of && and || first evaluate their left-hand expression, and if that fully determines the result (false for &&, true for ||) then the right-hand expression doesn’t need to be evaluated—and is guaranteed not to be. We all get so used to this handy feature that we routinely allow the correctness of the right-hand side depend on the success of the left-hand side: Employee* e = TryToGetEmployee(); if( e && e->Manager() ) // … This code’s correctness relies on the fact that e->Manager() will not be evaluated if e is null. This is perfectly usual and fine—unless the && used is an overloaded operator&&, because then the expression involving && will follow function rules instead: x Function calls always evaluate all arguments before execution. x The order of evaluation of function arguments is unspecified. (See also Item 31.) So let’s look at a modernized version of the snippet above that uses smart pointers: some_smart_ptr e = TryToGetEmployee(); if( e && e->Manager() ) // … Now, say this code happens to invoke an overloaded operator&& (provided by the author either of some_smart_ptr or of Employee). Then the code will still look fine to the reader, but will potentially (and disastrously) call e->Manager() when e is null. Some other code won’t dump core even in the presence of such eager evaluation, but becomes incorrect for a different reason if it depends on the order in which the two expressions are evaluated. The effects, of course, can be just as harmful. Consider:

Functions and Operators

53

if( DisplayPrompt() && GetLine() ) // … If operator&& is a user-defined operator, it is unspecified whether DisplayPrompt or GetLine is called first. The program could inadvertently end up waiting for input from the user before displaying the explanatory prompt. Of course, such code may seem to work with your current compiler and build settings. It’s still fragile. Compilers can (and do) choose whatever order they find fit best for any particular call, taking into account concerns such as generated code size, available registers, expression complexity, and so on. So the same call might behave differently depending on the compiler version, the compiler switch settings, and even on the statements surrounding the call. The same fragility occurs with the comma operator. Like && and ||, the built-in comma guarantees that its expressions will be evaluated left-to-right (unlike && and ||, it always evaluates both). A user-defined comma operator cannot guarantee leftto-right evaluation, usually with surprising results. For example, if the following code invokes a user-defined comma operator, it is unspecified whether g receives the value 0 or the value 1. int i = 0; f( i++ ), g( i );

// see also Item 31

Examples Example: Initialization library with overloaded operator, for sequence initialization. One library helpfully tried to make it easier to add multiple values to a container in one shot by overloading the comma. For example, to append to a vector letters: set_cont(letters) += “a”, “b”;

// problematic

That’s fine until the day the caller writes: set_cont(letters) += getstr(), getstr(); // order unspecified when using overloaded “,” If getstr gets user console input, for example, and the user enters the strings “c” and “d” in that order, the strings can actually be applied in either order. That’s a surprise, because this is not a problem for the built-in sequencing operator,: string s; s = getstr(), getstr();

// order well-specified using built-in “,”

Exceptions An exception is expression template libraries, which by design capture all operators.

References [Dewhurst03] §14 • [Meyers96] §7, §25 • [Murray93] §2.4.3 • [Stroustrup00] §6.2.2

54

C++ Coding Standards

31. Don’t write code that depends on the order of evaluation of function arguments. Summary Keep (evaluation) order: The order in which arguments of a function are evaluated is unspecified, so don’t rely on a specific ordering.

Discussion In the early days of C, processor registers were a precious resource, and compilers were hard pressed to allocate them efficiently for complex expressions in high-level languages. To allow generation of faster code, the creators of C gave the register allocator an extra degree of freedom: When calling a function, the order of evaluation of its arguments was left unspecified. That motivation is arguably less strong with today’s processors, but the fact remains that the order of evaluation is unspecified in C++ and, as it turns out, varies widely across compilers. (See also Item 30.) This can cause big trouble to the unwary. Consider this code: void Transmogrify( int, int ); int count = 5; Transmogrify( ++count, ++count );

// order of evaluation unknown

All we can say for certain is that count will be 7 as soon as Transmogrify’s body is entered—but we can’t say which of its arguments is 6 and which is 7. This uncertainty applies to much less obvious cases, such as functions that modify their argument (or some global state) as a side effect: int Bump( int& x ) { return ++x; } Transmogrify( Bump(count), Bump(count) ); // still unknown Per Item 10, avoid global and shared variables in the first place. But even if you avoid them, others’ code might not. For example, some standard functions do have side effects (e.g., strtok, and the various overloads of operatorFoo();

// invokes pD->Foo(1)

Base *pB = pD; pB->Foo();

// invokes pB->Foo(0)

67

It can be surprising to callers that the same object’s member function silently takes different arguments depending on the static type they happen to access it through. Prefer to add the redundant virtual when overriding a function. It makes the intent clearer to the reader. Beware of inadvertently hiding overloads in the base class. For example: class Base{ // … virtual void Foo( int ); virtual void Foo( int, int ); void Foo( int, int, int ); }; class Derived : public Base { // … virtual void Foo( int ); // overrides Base::Foo(int), but hides the others }; Derived d; d.Foo( 1 ); d.Foo( 1, 2 ); d.Foo( 1, 2, 3 );

// ok // error (oops?) // error (oops?)

If the base class’s overloads should be visible, write a using declaration to redeclare them in the derived class: class Derived : public Base { // … virtual void Foo( int ); // overrides Base::Foo(int) using Base::Foo; // bring the other Base::Foo overloads into scope };

Examples Example: Ostrich. If class Bird defines the virtual function Fly and you derive a new class Ostrich (a notoriously flightless bird) from Bird, how do you implement Ostrich::Fly? The answer is, “It depends.” If Bird::Fly guarantees success (i.e., provides the no-fail guarantee; see Item 71) because flying is an essential part of the Bird model, then Ostrich is not an adequate implementation of that model.

References [Dewhurst03] §73-74, §78-79 • [Sutter00] §21 • [Keffer95] p. 18

68

C++ Coding Standards

39. Consider making virtual functions nonpublic, and public functions nonvirtual. Summary In base classes with a high cost of change (particularly ones in libraries and frameworks): Prefer to make public functions nonvirtual. Prefer to make virtual functions private, or protected if derived classes need to be able to call the base versions. (Note that this advice does not apply to destructors; see Item 50.)

Discussion Most of us have learned through bitter experience to make class members private by default unless we really need to expose them. That’s just good encapsulation. This wisdom is applied most frequently to data members (see Item 41), but it applies equally to all members, including virtual functions. Particularly in OO hierarchies that are expensive to change, prefer full abstraction: Prefer to make public functions nonvirtual, and prefer to make virtual functions private (or protected if derived classes need to be able to call the base versions). This is the Nonvirtual Interface (NVI) pattern. (NVI is similar to other patterns, notably Template Method [Gamma95], but has a distinct motivation and purpose.) A public virtual function inherently has two different and competing responsibilities, aimed at two different and competing audiences: x It specifies interface: Being public, it is directly part of the interface the class presents to the rest of the world. x It specifies implementation detail: Being virtual, it provides a hook for derived classes to replace the base implementation of that function (if any); it is a customization point. Because these two responsibilities have different motives and audiences, they can be (and frequently are) in conflict, and then one function by definition cannot fulfill both responsibilities perfectly. That a public virtual function inherently has two significantly different jobs and two competing audiences is a sign that it’s not separating concerns well—including that it is inherently violating Items 5 and 11—and that we should consider a different approach. By separating public functions from virtual functions, we achieve the following significant benefits: x Each interface can take its natural shape: When we separate the public interface from the customization interface, each can easily take the form it naturally

Class Design and Inheritance

69

wants to take instead of trying to find a compromise that forces them to look identical. Often, the two interfaces want different numbers of functions and/or different parameters; for example, outside callers may call a single public Process function that performs a logical unit of work, whereas customizers may prefer to override only certain parts of the processing, which is naturally modeled by independently overridable virtual functions (e.g., DoProcessPhase1, DoProcessPhase2) so that derived classes aren’t forced to override everything. (This latter example specifically is the Template Method pattern arrived at from a different route.) x The base class is in control: The base class is now in complete control of its interface and policy and can enforce interface preconditions and postconditions (see Items 14 and 68), insert instrumentation, and do any similar work all in a single convenient reusable place—the nonvirtual interface function. This “prefactoring” for separation thus promotes good class design. x The base class is robust in the face of change: We are free to change our minds later and add pre- and postcondition checking, or separate processing into more steps, or refactor, or implement a fuller interface/implementation separation using the Pimpl idiom (see Item 43), or make other modifications to the base class’s customizability, without affecting any of the code that uses or inherits from the class. Note that it is much easier to start with NVI (even if the public function is just a one-line passthrough to the virtual function) and then later add checking or instrumentation, because that can then be done without breaking any code that uses or inherits from the class. It is much harder to start with a public virtual function and later have to break it apart, which necessarily breaks either the code that uses the class or the code that inherits from the class, depending on whether we choose to keep the original function as the virtual function or as the public function, respectively. See also Item 54.

Exceptions NVI does not apply to destructors because of their special order of execution (see Item 50). NVI does not directly support covariant return types for callers. If you need covariance that is visible to calling code without using dynamic_cast downcasts (see also Item 93), it’s easier to make the virtual function public.

References [Allison98] §10 • [Dewhurst03] §72 • [Gamma95] • [Keffer95 pp. 6-7] • [Koenig97] §11 • [Sutter00] §19, §23 • [Sutter04] §18

70

C++ Coding Standards

40. Avoid providing implicit conversions. Summary Not all change is progress: Implicit conversions can often do more damage than good. Think twice before providing implicit conversions to and from the types you define, and prefer to rely on explicit conversions (explicit constructors and named conversion functions).

Discussion Implicit conversions have two main problems: x They can fire in the most unexpected places. x They don’t always mesh well with the rest of the language. Implicitly converting constructors (constructors that can be called with one argument and are not declared explicit) interact poorly with overloading and foster invisible temporary objects that pop up all over. Conversions defined as member functions of the form operator T (where T is a type) are no better—they interact poorly with implicit constructors and can allow all sorts of nonsensical code to compile. Examples are embarrassingly numerous (see References). We mention only two (see Examples). In C++, a conversion sequence can include at most one user-defined conversion. However, when built-in conversions are added to the mix, the results can be extremely confusing. The solution is simple: x By default, write explicit on single-argument constructors (see also Item 54): class Widget { // … explicit Widget( unsigned int widgetizationFactor ); explicit Widget( const char* name, const Widget* other = 0 ); }; x Use named functions that offer conversions instead of conversion operators: class String { // … const char* as_char_pointer() const; };

// in the grand c_str tradition

See also the discussion of explicit copy constructors in Item 54.

Examples Example 1: Overloading. Say you have a Widget::Widget( unsigned int ) that can be invoked implicitly, and a Display function overloaded for Widgets and doubles. Consider the following overload resolution surprise:

Class Design and Inheritance void Display( double ); void Display( const Widget& );

// displays a double // displays a Widget

Display( 5 );

// oops: creates and displays a Widget

71

Example 2: Errors that work. Say you provide operator const char* for a String class: class String { // … public: operator const char*(); };

// deplorable form

Suddenly, a lot of silly expressions now compile. Assume s1, s2 are Strings: int x = s1 - s2; const char* p = s1 - 5; p = s1 + ‘0’; if( s1 == “0” ) { … }

// compiles; undefined behavior // compiles; undefined behavior // compiles; doesn’t do what you’d expect // compiles; doesn’t do what you’d expect

The standard string wisely avoids an operator const char* for exactly this reason.

Exceptions When used sparingly and with care, implicit conversions can make calling code short and intuitive. The standard std::string defines an implicit constructor that takes a const char*. This works fine because the designers took some precautions: x There is no automatic conversion to const char*; that conversion is provided through two named functions, c_str and data. x All comparison operators defined for std::string (e.g., ==, !=, t1_ and temp->t2_; if it fails then throw, else commit using: pimpl_ = temp; return *this; }

Exceptions Only add complexity, including Pimpls, when you know you benefit from the extra level of indirection. (See Items 6 and 8.)

References [Coplien92] §5.5 • [Dewhurst03] §8 • [Lakos96] §6.4.2 • [Meyers97] §34 • [Murray93] §3.3 • [Stroustrup94] §2.10, §24.4.2 • [Sutter00] §23, §26-30 • [Sutter02] §18, §22 • [Sutter04] §16-17

Class Design and Inheritance

79

44. Prefer writing nonmember nonfriend functions. Summary Avoid membership fees: Where possible, prefer making functions nonmember nonfriends.

Discussion Nonmember nonfriend functions improve encapsulation by minimizing dependencies: The body of the function cannot come to depend on the nonpublic members of the class (see Item 11). They also break apart monolithic classes to liberate separable functionality, further reducing coupling (see Item 33). They improve genericity, because it’s hard to write templates that don’t know whether or not an operation is a member for a given type (see Item 67). Use this algorithm to determine whether a function should be a member and/or friend: // If you have no choice then you have no choice; make it a member if it must be: If the function is one of the operators =, ->, [], or (), which must be members: Make it a member. // If it can be a nonmember nonfriend, or benefits from being a nonmember friend, do it: Else if: a) the function needs a different type as its left-hand argument (as do operators >> or ~T(); new (this) T( rhs ); } return *this; }

// bad: an anti-idiom // this technique is evil // (see [Sutter00] §41)

Prefer to provide a nonmember swap function in the same namespace as your type when objects of your type have a way to exchange their values more efficiently than via brute-force assignment, such as if they have their own swap or equivalent function (see Item 57). Additionally, consider specializing std::swap for your own nontemplate types: namespace std { template void swap( MyType& lhs, MyType& rhs) { lhs.swap( rhs ); } }

// for MyType objects, // use MyType::swap

The standard does not allow you to do this when MyType is itself a template class. Fortunately, this specialization is just a nice-to-have; the primary technique is to provide a type-customized swap as a nonmember in the same namespace as the type.

Exceptions Swapping is valuable for classes with value semantics. It is less often useful for base classes because you use those classes through pointers anyway. (See Items 32 and 54.)

References [C++03] §17.4.3.1(1) • [Stroustrup00] §E.3.3 • [Sutter00] §12-13, §41

This page intentionally left blank

Namespaces and Modules Systems have sub-systems and sub-systems have sub-systems and so on ad infinitum—which is why we’re always starting over. —Alan Perlis

The namespace is an important tool for managing names and reducing name collisions. So is a module, which is additionally an important tool for managing releases and versioning. We define a module as any cohesive unit of release (see Item 5) maintained by the same person or team; typically, a module is also consistently compiled with the same compiler and switch settings. Modules exist at many levels of granularity that range widely in size; a module can be as small as a single object file that delivers a single class, to a single shared or dynamic library generated from multiple source files whose contents form a subsystem inside a larger application or are released independently, to as large as a huge library composed of many smaller modules (e.g., shared libraries, DLLs, or other libraries) and containing thousands of types. Even though such entities as shared libraries and dynamic libraries are not mentioned directly in the C++ Standard, C++ programmers routinely build and use libraries, and good modularization is a fundamental part of successful dependency management (see for example Item 11). It’s hard to imagine a program of any significant size that doesn’t use both namespaces and modules. In this section, we cover basic guidelines on using these two related management and bundling tools with a view to how they interact well or badly with the rest of the C++ language and run-time environment. These rules and guidelines show how to maximize the “well” and avoid the “badly.” Our vote for the most valuable Item in this section goes to Item 58: Keep types and functions in separate namespaces unless they’re specifically intended to work together.

103

104

C++ Coding Standards

57. Keep a type and its nonmember function interface in the same namespace. Summary Nonmembers are functions too: Nonmember functions that are designed to be part of the interface of a class X (notably operators and helper functions) must be defined in the same namespace as the X in order to be called correctly.

Discussion Both public member functions and nonmember functions form part of the public interface of a class. The Interface Principle states: For a class X, all functions (including nonmember functions) that both “mention” X and are “supplied with” X in the same namespace are logically part of X, because they form part of X’s interface. (See Item 44 and [Sutter00].) The C++ language is explicitly designed to enforce the Interface Principle. The reason why argument-dependent lookup (ADL, also known as Koenig lookup) was added to the language was to ensure that code that uses an object x of type X can use its nonmember function interface (e.g., cout f( 5 ): If p is a pointer to a Base class type, this calls a specific interface function, such as perhaps a virtual int f(int). But if p is of a generic type, this call can bind to a myriad of things, including that it might invoke an overloaded operator-> that returns a type defining the function X f(double) where X is convertible to int. x Static type checking: All types are checked statically. x Static binding (prevents separate compilation): All types are bound statically. x Efficiency: Compile-time evaluation and static binding allow optimizations and efficiencies not available with dynamic binding. Decide on your priorities, and use each type of polymorphism for its strengths. Prefer to blend both kinds of polymorphism to combine their benefits while trying not to combine their drawbacks: x Static helps dynamic: Use static polymorphism to implement dynamically polymorphic interfaces. For example, you might have an abstract base class Command and define various implementations as template class ConcreteCommand : public Command. Examples include implementing the Command and Visitor design patterns (see [Alexandrescu01] and [Sutter04]). x Dynamic helps static: Offer a generic, comfortable, statically bound interface, but internally dispatch dynamically, so you offer a uniform object layout. Good examples are discriminated union implementations (see [Alexandrescu02b] and [Boost]) and tr1::shared_ptr’s Deleter parameter (see [C++TR104]). x Any other blend: A bad blend that combines the weaknesses of both is worse than either alone; a good blend that combines the benefits of both is better than either alone. For example, don’t put virtual functions into a class template unless you want all virtual functions to be instantiated every time (this is in sharp contrast to nonvirtual functions of templated types). The code size hit can be astronomical, and you may overconstrain your generic type by instantiating functionality that is never needed. The standard facets made this mistake. Don’t make it again.

References [Alexandrescu01] §10 • [Alexandrescu02b] • [C++TR104] • [Gamma95] • [Musser01] §1.2-3, §17 • [Stroustrup00] §24.4.1 • [Sutter00] §3 • [Sutter02] §1 • [Sutter04] §17, §35 • [Vandevoorde03] §14 • [Webber03] §8.6

122

C++ Coding Standards

65. Customize intentionally and explicitly. Summary Intentional is better than accidental, and explicit is better than implicit: When writing a template, provide points of customization knowingly and correctly, and document them clearly. When using a template, know how the template intends for you to customize it for use with your type, and customize it appropriately.

Discussion A common pitfall when writing template libraries is providing unintentional points of customization—that is, points where a caller’s code can get looked up and used inside your template, but you didn’t mean for a caller’s code to get involved. It’s easy to do: Just call another function or operator the normal way (unqualified), and if one of its arguments happens to be of a template parameter type (or a related type) then ADL will pick it up. Examples abound: See Item 58 for an example. Instead, be intentional: Know the three major ways to provide points of customization in a template, decide which one you want to use at a given point in your template, and code it correctly. Then, check to verify that that you didn’t accidentally also code a customization hook in places where you didn’t mean to. The first way to provide a point of customization is the usual “implicit interface” (see Item 64) approach where your template simply relies on a type’s having an appropriate member with a given name: // Option 1: Provide a point of customization by requiring T to provide “foo-ability” // as a member function with a given name, signature, and semantics. template void Sample1( T t ) { t.foo(); }

// foo is a point of customization

typename T::value_type x; // another example: providing a point of custom// ization to look up a type (usually via typedef)

To implement Option 1, the author of Sample1 must: x Call the function with member notation: Just use the natural member syntax. x Document the point of customization: The type must provide an accessible member function foo that can be called with given arguments (here, none). The second option is to use the “implicit interface” method, but with a nonmember function that is looked up via argument-dependent lookup (i.e., it is expected to be in the namespace of the type with which the template is instantiated); this is a major motivation for the language’s ADL feature (see Item 57). Your template is relying on a type’s having an appropriate nonmember with a given name:

Templates and Genericity

123

// Option 2: Provide a point of customization by requiring T to provide “foo-ability” // as a nonmember function, typically looked up by ADL, with a given name, signature, // and semantics. (This is the only option that doesn’t also work to look up a type.) template void Sample2( T t ) { foo( t ); }

cout baz(); } };

// call base member function // alternative

The C++ standard library generally favors relying on Option 2 (e.g., ostream_iterators look up operator) should fail to compile. Even if it does compile, never write that; if you want to write it, you almost certainly want a container of shared_ptrs instead. Example 2: Heterogeneous containers. To have a container store and own objects of different but related types, such as types derived from a common Base class, prefer container< shared_ptr >. An alternative is to store proxy objects whose nonvirtual functions pass through to corresponding virtual functions of the actual object. Example 3: Containers of non-value types. To contain objects even though they are not copyable or otherwise not value-like (e.g., DatabaseLocks and TcpConnections), prefer containing them indirectly via smart pointers (e.g., container< shared_ptr > and container< shared_ptr >). Example 4: Optional values. When you want a map, but some Things have no associated Widget, prefer map. Example 5: Index containers. To have a main container hold the objects and access them using different sort orders without resorting the main container, you can set up secondary containers that “point into” the main one and sort the secondary containers in different ways using dereferenced compare predicates. But prefer a container of MainContainer::iterators (which are value-like) instead of a container of pointers.

References [Allison98] §14 • [Austern99] §6 • [Dewhurst03] §68 • [Josuttis99] §5.10.2 • [Koenig97] §5 • [Meyers01] §3, §7-8 • [SuttHysl04b]

STL: Containers

155

80. Prefer push_back to other ways of expanding a sequence. Summary push_back all you can: If you don’t need to care about the insert position, prefer using push_back to add an element to a sequence. Other means can be both vastly slower and less clear.

Discussion You can insert elements into sequences at different points using insert, and you can append elements to sequences in various ways including: vector vec; vec.resize(vec.size() + 1, 1); vec.insert(vec.end(), 2); vec.push_back(3);

// vec is empty // vec contains { 1 } // vec contains { 1, 2 } // vec contains { 1, 2, 3 }

Of all forms, push_back alone takes amortized constant time. The other forms’ performance can be as bad as quadratic. Needless to say, beyond small data volumes that makes those alternatives potential scalability barriers. (See Item 7.) push_back’s magic is simple: It expands capacity exponentially, not by a fixed increment. Hence the number of reallocations and copies decreases rapidly with size. For a container populated using only push_back calls, on average each element has been copied only once—regardless of the final size of the container. Of course, resize and insert could employ the same strategy, but that is dependent on the implementation; only push_back offers the guarantee. Algorithms can’t call push_back directly because they don’t have access to the container. You can ask algorithms to use push_back anyway by using a back_inserter.

Exceptions If you know you’re adding a range, even at the end of a container, prefer to use a range insertion function (see Item 81). Exponential growth is generous with memory allocation. To fine-tune growth, call reserve explicitly—push_back, resize, and the like never trigger reallocation if they have enough space. To “right-size” a vector, use the shrink-to-fit idiom (see Item 82).

References [Stroustrup00] §3.7-8, §16.3.5, §17.1.4.1

156

C++ Coding Standards

81. Prefer range operations to single-element operations. Summary Don’t use oars when the wind is fair (based on a Latin proverb): When adding elements to sequence containers, prefer to use range operations (e.g., the form of insert that takes a pair of iterators) instead of a series of calls to the single-element form of the operation. Calling the range operation is generally easier to write, easier to read, and more efficient than an explicit loop. (See also Item 84.)

Discussion The more context you can give a function, the better the chances that it can do something useful with the information. In particular, when you call a single function and pass it a pair of iterators first and last that delimit a range, it can perform optimizations based on knowing the number of objects that are going to be added, which it obtains by computing distance(first,last). The same applies to “repeat n times” operations, such as the vector constructor that takes a repeat count and a value.

Examples Example 1: vector::insert. Let’s say you want to add n elements into a vector v. Calling v.insert(position,x) repeatedly can cause multiple reallocations as v grows its storage to accommodate each new element. Worse, each single-element insert is a linear operation because it has to shuffle over enough elements to make room, and this makes inserting n elements with repeated calls to the single-element insert actually a quadratic operation! Of course, you could get around the multiple-reallocation problem by calling reserve, but that doesn’t reduce the shuffling and the quadratic nature of the operation. It’s faster and simpler to just say what you’re doing: v.insert(position,first,last), where first and last are iterators delimiting the range of elements to be added into v. (If first and last are input iterators, there’s no way to determine the size of the range before actually traversing it, and therefore v might still need to perform multiple reallocations; but the range version is still likely to perform better than inserting elements individually.) Example 2: Range construction and assignment. Calling a constructor (or assign function) that takes an iterator range typically performs better than calling the default constructor (or clear) followed by individual insertions into the container.

References [Meyers01] §5 • [Stroustrup00] §16.3.8

STL: Containers

157

82. Use the accepted idioms to really shrink capacity and really erase elements. Summary Use a diet that works: To really shed excess capacity from a container, use the “swap trick.” To really erase elements from a container, use the erase-remove idiom.

Discussion Some containers (e.g., vector, string, deque) can end up carrying around extra capacity that’s no longer needed. Although the C++ standard library containers provide no guaranteed way to trim excess capacity, the following “swap trick” idiom works in practice to get rid of excess capacity for such a c of type container: container( c ).swap( c ); // the shrink-to-fit idiom to shed excess capacity Or, to empty c out completely, clearing all contained elements and shedding all possible capacity, the idiom is: container().swap( c );

// the idiom to shed all contents and capacity

In related news, a common surprise for new STL programmers is that the remove algorithm doesn’t really remove elements from a container. Of course, it can’t; an algorithm just works on an iterator range, and you can’t actually remove something from a container without calling a container member function, usually erase. All remove does is to shuffle values around so that the elements that shouldn’t be “removed” get shuffled up toward the beginning of the range, and return an iterator to one past the end of the unremoved elements. To really get rid of them, the call to remove needs to be followed by a call to erase—hence the “erase-remove” idiom. For example, to erase all elements equal to value from a container c, you can write: c.erase( remove( c.begin(), c.end(), value ), c.end() ); Prefer to use a member version of remove or remove_if on a container that has it.

Exceptions The usual shrink-to-fit idiom won’t work on copy-on-write implementations of std::string. What usually does work is to call s.reserve(0) or to fake the string out by writing string(s.begin(), s.end()).swap(s); to use the iterator range constructor. In practice, these work to shed excess capacity. (Better still, std::string implementations are abandoning copy-on-write, which is an outdated optimization; see [Sutter02].)

References [Josuttis99] §6.2.1 • [Meyers01] §17, §32, §44 • [Sutter00] §7 • [Sutter02] §7, §16

This page intentionally left blank

STL: Algorithms Prefer algorithms to loops. —Bjarne Stroustrup, [Stroustrup00] §18.12

Algorithms are loops—only better. Algorithms are the “patterns” of loops, adding far more semantic content and richness than a naked for would do alone. Of course, the moment you start using algorithms you’ll also start using function objects and predicates; write them correctly, and use them well. Our vote for the most valuable Item in this section goes to Item 83: Use a checked STL implementation.

159

160

C++ Coding Standards

83. Use a checked STL implementation. Summary Safety first (see Item 6): Use a checked STL implementation, even if it’s only available for one of your compiler platforms, and even if it’s only used during pre-release testing.

Discussion Just like pointer mistakes, iterator mistakes are far too easy to make and will usually silently compile but then crash (at best) or appear to work (at worst). Even though your compiler doesn’t catch the mistakes, you don’t have to rely on “correction by visual inspection,” and shouldn’t: Tools exist. Use them. Some STL mistakes are distressingly common even for experienced programmers: x Using an invalidated or uninitialized iterator: The former in particular is easy to do. x Passing an out-of-bounds index: For example, accessing element 113 of a 100element container. x Using an iterator “range” that isn’t really a range: Passing two iterators where the first doesn’t precede the second, or that don’t both refer into the same container. x Passing an invalid iterator position: Calling a container member function that takes an iterator position, such as the position passed to insert, but passing an iterator that refers into a different container. x Using an invalid ordering: Providing an invalid ordering rule for ordering an associative container or as a comparison criterion with the sorting algorithms. (See [Meyers01] §21 for examples.) Without a checked STL, these would typically manifest at run time as erratic behavior or infinite loops, not as hard errors. Most checked STL implementations detect these errors automatically, by adding extra debugging and housekeeping information to containers and iterators. For example, an iterator can remember the container it refers into, and a container can remember all outstanding iterators into itself so that it can mark the appropriate iterators as invalid as they become invalidated. Of course, this makes for fatter iterators, containers with extra state, and some extra work every time you modify the container. But it’s worth it—at least during testing, and perhaps even during release (remember Item 8; don’t disable valuable checks for performance reasons unless and until you know performance is an issue in the affected cases). Even if you don’t ship with checking turned on, and even if you only have a checked STL on one of your target platforms, at minimum ensure that you routinely run your full complement of tests against a version of your application built with a checked STL. You’ll be glad you did.

STL: Algorithms

161

Examples Example 1: Using an invalid iterator. It’s easy to forget when iterators are invalidated and use an invalid iterator (see Item 99). Consider this example adapted from [Meyers01] that inserts elements at the front of a deque: deque::iterator current = d.begin(); for( size_t i = 0; i < max; ++i ) d.insert( current++, data[i] + 41 );

// do you see the bug?

Quick: Do you see the bug? You have three seconds.—Ding! If you didn’t get it in time, don’t worry; it’s a subtle and understandable mistake. A checked STL implementation will detect this error for you on the second loop iteration so that you don’t need to rely on your unaided visual acuity. (For a fixed version of this code, and superior alternatives to such a naked loop, see Item 84.) Example 2: Using an iterator range that isn’t really a range. An iterator range is a pair of iterators first and last that refer to the first element and the one-past-the-end-th element of the range, respectively. It is required that last be reachable from first by repeated increments of first. There are two common ways to accidentally try to use an iterator range that isn’t actually a range: The first way arises when the two iterators that delimit the range point into the same container, but the first iterator doesn’t actually precede the second: for_each( c.end(), c.begin(), Something );

// not always this obvious

On each iteration of its internal loop, for_each will compare the first iterator with the second for equality, and as long as they are not equal it will continue to increment the first iterator. Of course, no matter how many times you increment the first iterator, it will never equal the second, so the loop is essentially endless. In practice, this will, at best, fall off the end of the container c and crash immediately with a memory protection fault. At worst, it will just fall off the end into uncharted memory and possibly read or change values that aren’t part of the container. It’s not that much different in principle from our infamous and eminently attackable friend the buffer overrun. The second common case arises when the iterators point into different containers: for_each( c.begin(), d.end(), Something );

// not always this obvious

The results are similar. Because checked STL iterators remember the containers that they refer into, they can detect such run-time errors.

References [Dinkumware-Safe] • [Horstmann95] • [Josuttis99] §5.11.1 • [Metrowerks] • [Meyers01] §21, §50 • [STLport-Debug] • [Stroustrup00] §18.3.1, §19.3.1

162

C++ Coding Standards

84. Prefer algorithm calls to handwritten loops. Summary Use function objects judiciously: For very simple loops, handwritten loops can be the simplest and most efficient solution. But writing algorithm calls instead of handwritten loops can be more expressive and maintainable, less error-prone, and as efficient. When calling algorithms, consider writing your own custom function object that encapsulates the logic you need. Avoid cobbling together parameter-binders and simple function objects (e.g., bind2nd, plus), which usually degrade clarity. Consider trying the [Boost] Lambda library, which automates the task of writing function objects.

Discussion Programs that use the STL tend to have fewer explicit loops than non-STL programs, replacing low-level semantics-free loops with higher-level and better-defined abstract operations that convey greater semantic information. Prefer a “process this range” algorithmic mindset over “process each element” loopy thinking. A primary benefit that algorithms and design patterns have in common is that they let us speak at a higher level of abstraction with a known vocabulary. These days, we don’t say “let many objects watch one object and get automatic notifications when its state changes;” rather, we say just “Observer.” Similarly, we say “Bridge,” “Factory,” and “Visitor.” Our shared pattern vocabulary raises the level, effectiveness, and correctness of our discussion. With algorithms, we likewise don’t say “perform an action on each element in a range and write the results somewhere;” rather, we say transform. Similarly, we say for_each, replace_if, and partition. Algorithms, like design patterns, are self-documenting. Naked for and while loops just don’t do when it comes to imparting any inherent semantic information about the purpose of the loop; they force readers to inspect their loop bodies to decipher what’s going on. Algorithms are also more likely to be correct than loops. Handwritten loops easily make mistakes such as using invalidated iterators (see Items 83 and 99); algorithms come already debugged for iterator invalidation and other common errors. Finally, algorithms are also often more efficient than naked loops (see [Sutter00] and [Meyers01]). They avoid needless minor inefficiencies, such as repeated evaluations of container.end(). Slightly more importantly, the standard algorithms you’re using were implemented by the same people who implemented the standard containers you’re using, and by dint of their inside knowledge those people can write algorithms that are more efficient than any version you would write. Most important of all, however, is that many algorithms have highly sophisticated implementations that we in-the-trenches programmers are unlikely ever to match in handwritten code (unless we don’t need the full generality of everything a given algorithm does).

STL: Algorithms

163

In general, the more widely used a library is, the better debugged and more efficient it will be simply because it has so many users. You are unlikely to be using any other library that is as widely used as your standard library implementation. Use it and benefit. STL’s algorithms are already written; why write them again? Consider trying [Boost] lambda functions. Lambda functions are an important tool that solves the biggest drawback of algorithms, namely readability: They write the function objects for you, leaving the actual code in place at the call point. Without lambda functions, your choices are to either use function objects (but then even simple loop bodies live in a separate place away from the call point) or else use the standard binders and function objects such as bind2nd and plus (these are tedious and confusing to use, and hard to combine because fundamental operations like compose are not part of the standard; but do consider the [C++TR104] bind library).

Examples Here are two examples adapted from [Meyers01]: Example 1: Transforming a deque. After working through several incorrect iterations that ran afoul of iterator invalidation issues (e.g., see Item 83), we finally come up with the following correct handwritten loop for adding 41 to every element of data, an array of doubles, and placing the result at the beginning of d, a deque: deque::iterator current = d.begin(); for( size_t i = 0; i < max; ++i ) { current = d.insert( current, data[i] + 41 ); ++current; }

// be careful to keep current valid… // … then increment it when it’s safe

An algorithm call would have bypassed the correctness pitfalls straight away: transform( data.begin(), data.end(), // copy elements from data inserter(d, d.begin()), // to the front of d bind2nd(plus(), 41) ); // adding 41 to each Granted, bind2nd and plus are awkward. Frankly, nobody really uses them much, and that’s just as well because they hurt readability (see Item 6). But lambda functions, which generate the function object for us, let us write simply: transform( data, data + max, inserter( d, d.begin() ), _1 + 41 ); Example 2: Find the first element between x and y. Consider this naked loop that searches a vector v for the first value between x and y, by calculating an iterator that points to the found element or to v.end(): vector::iterator i = v.begin(); for( ; i != v.end(); ++i ) if( *i > x && *i < y ) break;

164

C++ Coding Standards

An algorithm call is problematic. Absent lambdas, the two options are to write a custom function object or to use the standard binders. Alas, with the binders option we can’t use the standard binders alone but need to use the nonstandard (although widely-available) compose2 adapter, and even at that the code is just impenetrable, and nobody would ever really write it: vector::iterator i = find_if( v.begin(), v.end(), compose2( logical_and(), bind2nd(greater(), x), bind2nd(less(), y) ) ); The other option, namely writing a custom function object, is viable. It looks good at the call point, and its main drawback is that it requires writing a BetweenValues function object that moves the body’s logic visually away from the call point: template class BetweenValues : public unary_function { public: BetweenValues( const T& low, const T& high ) : low_(low), high_(high) { } bool operator()( const T& val ) const { return val > low_ && val < high_; } private: T low_, high_; };

// far away from the point of use

vector::iterator i = find_if( v.begin(), v.end(), BetweenValues(x, y) ); Lambda functions, which generate the function object for us, let us write simply: vector::iterator i = find_if( v.begin(), v.end(), _1 > x && _1 < y );

Exceptions When used with function objects, algorithm calls place the body of the loop away from the call site, which can make the loop harder to read. (Cobbling together simple objects with the standard and nonstandard binders isn’t a realistic option.) [Boost] lambda functions solve these two problems and work reliably on modern compilers, but they don’t work on older compilers and they can generate dense error messages when they’re coded incorrectly. Calling named functions, including member functions, still requires binder-like syntax.

References [Allison98] §15 • [Austern99] §11-13 • [Boost] Lambda library • [McConnell93] §15 • [Meyers01] §43 • [Musser01] §11 • [Stroustrup00] §6.1.8, §18.5.1 • [Sutter00] §7

STL: Algorithms

165

85. Use the right STL search algorithm. Summary Search “just enough”—the right search may be STL (slower than light), but it’ll still be pretty fast: This Item applies to searching for a particular value in a range, or for the location where it would be if it were in the range. To search an unsorted range, use find/find_if or count/count_if. To search a sorted range, use lower_bound, upper_bound, equal_range, or (rarely) binary_search. (Despite its common name, binary_search is usually not the right choice.)

Discussion For unsorted ranges, find/find_if and count/count_if can tell you in linear time whether and where, respectively, an element exists in a range. Note that find/find_if is typically more efficient because it can stop searching when a match is found. For sorted ranges, prefer the four binary search algorithms, binary_search, lower_bound, upper_bound, and equal_range, which work in logarithmic time. Alas, despite its nice name, binary_search is nearly always useless because it only returns a bool indicating whether a match was found or not. Usually, you want lower_bound or upper_bound—or equal_range, which gives you the results of both lower_bound and upper_bound (but not at twice the cost). lower_bound returns an iterator pointing to the first match (if there is one) or the location where it would be (if there is not); the latter is useful to find the right place to insert new values into a sorted sequence. upper_bound returns an iterator pointing to one past the last match (if there is one), which is the location where the next equivalent element can be added; this is useful to find the right place to insert new values into a sorted sequence while keeping equivalent elements in the order in which they were inserted. Prefer p = equal_range( first, last, value ); distance( p.first, p.second ); as a faster version of count( first, last, value ); for sorted ranges. If you are searching an associative container, prefer using the member functions with the same names instead of the nonmember algorithms. The member versions are usually more efficient, including that the member version of count runs in logarithmic time (and so there’s no motivation to replace a call to the member count with a call to equal_range followed by distance, as there is with the nonmember count).

References [Austern99] §13.2-3 • [Bentley00] §13 • [Meyers01] §34, §45 • [Musser01] §22.2 • [Stroustrup00] §17.1.4.1, §18.7.2

166

C++ Coding Standards

86. Use the right STL sort algorithm. Summary Sort “just enough:” Understand what each of the sorting algorithms does, and use the cheapest algorithm that does what you need.

Discussion You don’t always need a full sort; you usually need less, and rarely you need more. In general order from cheapest to most expensive, your standard sorting algorithm options are: partition, stable_partition, nth_element, partial_sort (with its variant partial_sort_copy), sort, and stable_sort. Use the least expensive one that does the work you actually need; using a more powerful one is wasteful. partition, stable_partition, and nth_element run in linear time, which is nice. nth_element, partial_sort, sort, and stable_sort require random-access iterators. You can’t use them if you have only bidirectional iterators (e.g., list::iterators). If you need these algorithms but you don’t have random-access iterators, consider using the index container idiom: Create a container that does support random-access iterators (e.g., a vector) of iterators into the range you have, and then use the more powerful algorithm on that using a dereferencing version of your predicate (one that dereferences the iterators before doing its usual comparison). Use the stable_… versions only if you need to preserve the relative ordering of equal elements. Note that partial_sort and nth_element aren’t stable (meaning that they don’t keep equivalent elements in the same relative order they were in before the sort), and they have no standardized stable versions. If you otherwise want these algorithms but need stability, you probably want stable_sort. Of course, don’t use any sorting algorithm at all if you don’t have to: If you are using a standard associative container (set/multiset or map/multimap) or the priority_queue adapter, and only need one sort order, the elements in the container stay sorted all the time.

Examples Example 1: partition. Use partition to just divide the range into two groups: all elements that satisfy the predicate, followed by all elements that don’t. This is all you need to answer questions like these:

STL: Algorithms

167

x “Who are all the students with a grade of B+ or better?” For example, partition( students.begin(), students.end(), GradeAtLeast(“B+”) ); does the work and returns an iterator to the first student whose grade is not at least B+. x “What are all the products with weight less than 10kg?” For example, partition( products.begin(), products.end(), WeightUnder(10) ); does the work and returns an iterator to the first product whose weight is 10kg or more. Example 2: nth_element. Use nth_element to put a single element in the correct n-th position it would occupy if the range were completely sorted, and with all other elements correctly preceding or following that n-th element. This is all you need to answer questions like these: x “Who are my top 20 salespeople?” For example, nth_element( s.begin(), s.begin()+19, s.end(), SalesRating ); puts the 20 best elements at the front. x “What is the item with the median level of quality in this production run?” That element would be in the middle position of a sorted range. To find it, do nth_element( run.begin(), run.begin()+run.size()/2, run.end(), ItemQuality );. x “What is the item whose quality is at the 75th percentile?” That item would be 25% of the way through the sorted range. To find it, do nth_element( run.begin(), run.begin()+run.size()*.25, run.end(), ItemQuality );. Example 3: partial_sort. partial_sort does the work of nth_element, plus the elements preceding the n-th are all in their correct sorted positions. Use partial_sort to answer questions similar to those nth_element answers, but where you need the elements that match to be sorted (and those that don’t match don’t need to be sorted). This is all you need to answer questions like, “Who are the first-, second-, and third-place winners?” For example, partial_sort( contestants.begin(), contestants.begin()+3, contestants.end(), ScoreCompare ); puts the top three contestants, in order, at the front of the container—and no more.

Exceptions Although partial_sort is usually faster than a full sort because it has to do less work, if you are going to be sorting most (or all) of the range, it can be slower than a full sort.

References [Austern99] §13.1 • [Bentley00] §11 • [Josuttis99] §9.2.2 • [Meyers01] §31 • [Musser01] §5.4, §22.26 • [Stroustrup00] §17.1.4.1, §18.7

168

C++ Coding Standards

87. Make predicates pure functions. Summary Predicate purity: A predicate is a function object that returns a yes/no answer, typically as a bool value. A function is pure in the mathematical sense if its result depends only on its arguments (note that this use of “pure” has nothing to do with pure virtual functions). Don’t allow predicates to hold or access state that affects the result of their operator(), including both member and global state. Prefer to make operator() a const member function for predicates (see Item 15).

Discussion Algorithms make an unknowable number of copies of their predicates at unknowable times in an unknowable order, and then go on to pass those copies around while casually assuming that the copies are all equivalent. That is why it’s your responsibility to make sure that copies of predicates are indeed all equivalent, and that means that they must be pure functions: functions whose result is not affected by anything other than the arguments passed to operator(). Additionally, predicates must also consistently return the same result for the same set of arguments they are asked to evaluate. Stateful predicates may seem useful, but they are explicitly not very useful with the C++ standard library and its algorithms, and that is intentional. In particular, stateful predicates can only be useful if: x The predicate is not copied: The standard algorithms make no such guarantee, and in fact assume that predicates are safely copyable. x The predicate is applied in a documented deterministic order: The standard algorithms generally make no guarantee about the order in which the predicate will be applied to the elements in the range. In the absence of guarantees about the order in which objects will be visited, operations like “flag the third element” (see Examples) make little sense, because which element will be visited “third” is not well-defined. It is possible to work around the first point by writing a lightweight predicate that uses reference-counting techniques to share its deep state. That solves the predicatecopying problem too because the predicate can be safely copied without changing its semantics when it is applied to objects. (See [Sutter02].) It is not possible, however, to work around the second point.

STL: Algorithms

169

Always declare a predicate type’s operator() as a const member function so that the compiler can help you avoid this mistake by emitting an error if you try to change any data members that the predicate type may have. This won’t prevent all abuses— for example, it won’t flag accesses to global data—but it will help the compiler to help you avoid at least the most common mistakes.

Examples Example: FlagNth. Here is the classic example from [Sutter02], which is intended to remove the third element from a container v: class FlagNth { public: FlagNth( size_t n ) : current_(0), n_(n) { } // evaluate to true if and only if this is the n-th invocation template bool operator()( const T& ) { return ++current_ == n_; }

// bad: non-const

private: size_t current_, n_; }; // … later … v.erase( remove_if( v.begin(), v.end(), FlagNth(3) ) ); This is not guaranteed to remove the third element, even though that was intended. In most real-world STL implementations, it erases both the third and the sixth elements. Why? Because remove_if is commonly implemented in terms of find_if and remove_copy_if, and it passes along a copy of the predicate to each of those functions, possibly after it has already itself done some work that affects the predicate’s state. Conceptually, this example is perverse because remove_if only guarantees that it will remove all elements satisfying some criterion. It does not document the order in which elements in the range will be visited or removed, so the code is relying on an assumed, but undocumented and unsatisfied, assumption. The correct way to remove a particular element is to iterate to it and then call erase.

References [Austern99] §4.2.2 • [Josuttis99] §5.8.2, §8.1.4 • [Meyers01] §39 • [Stroustrup00] §10.2.6 • [Sutter02] §2-3

170

C++ Coding Standards

88. Prefer function objects over functions as algorithm and comparer arguments. Summary Objects plug in better than functions: Prefer passing function objects, not functions, to algorithms. Comparers for associative containers must be function objects. Function objects are adaptable and, counterintuitively, they typically produce faster code than functions.

Discussion First, function objects are easy to make adaptable, and always should be (see Item 89). Even if you already have a function, sometimes you have to wrap it in ptr_fun or mem_fun anyway to add adaptability. For example, you have to do this in order to build up more complex expressions using binders (see also Item 84): inline bool IsHeavy( const Thing& ) { /*…*/ } find_if( v.begin(), v.end(), not1( IsHeavy ) );

// error: isn’t adaptable

The usual workaround is to insert ptr_fun (or, for a member function, mem_fun or mem_fun_ref), which unfortunately doesn’t work in this particular case: inline bool IsHeavy( const Thing& ) { /*…*/ } find_if( v.begin(), v.end(), not1( ptr_fun( IsHeavy ) ) ); // a valiant attempt It’s a pain that this wouldn’t work even if you explicitly specified ptr_fun’s template arguments. Briefly, the problem is that ptr_fun deduces the argument and return types exactly (in particular, the parameter type is deduced as const Thing&) and goes on to create internal machinery which, along the way, in turn helpfully tries to add another &, and references to references are not currently allowed by ISO C++. There are ways in which the standard language and/or library could, and probably should, be fixed so as to make this work correctly (e.g., by allowing references to references to collapse to a single reference; or see also Item 89), but it doesn’t do that today. You don’t have to remember this stuff if you’re using a correctly-written function object (see Item 89), which is adaptable from the get-go without special syntax: struct IsHeavy : unary_function { bool operator()( const Thing& ) const { /*…*/ } }; find_if( v.begin(), v.end(), not1( IsHeavy() ) );

// ok: adaptable

STL: Algorithms

171

More importantly, you need a function object, not a function, to specify comparers for associative containers. This is because it’s illegal to instantiate a template type parameter with a function directly: bool CompareThings( const Thing&, const Thing& ); set s;

// error

Instead, you need: struct CompareThings : public binary_function { bool operator()( const Thing&, const Thing& ) const; }; set s;

// ok

Finally, there is also an efficiency benefit. Consider this familiar algorithm: template Iter find_if( Iter first, Iter last, Compare comp ); If we pass a function as the comparer to find_if inline bool Function( const Thing& ) { /*…*/ } find_if( v.begin(), v.end(), Function ); we’re actually passing a reference to Function. Compilers rarely inline such function calls (except as part of whole-program analysis, which is still a relatively recent feature on popular compilers), even when as above the function is declared inline and is visible while compiling the find_if call. And, as noted, functions aren’t adaptable. If we pass a function object as the comparer to find_if struct FunctionObject : unary_function { bool operator()( const Thing& ) const { /*…*/ } }; find_if( v.begin(), v.end(), FunctionObject() ); we’re passing an object that typically has an (implicitly or explicitly) inline operator() function. Compilers have routinely inlined such calls since C++’s Bronze Age. Note: This is not to encourage premature optimization (see Item 8), but to discourage premature pessimization (see Item 9). If you already have a function, go ahead and pass a pointer to the function (unless you have to wrap it with ptr_fun or mem_fun anyway). But if you’re writing a new piece of code for use as an argument to an algorithm, prefer writing the extra boilerplate to make it a function object.

References [Austern99] §4, §8, §15 • [Josuttis99] §5.9 • [Meyers01] §46 • [Musser01] §8 • [Sutter04] §25

172

C++ Coding Standards

89. Write function objects correctly. Summary Be cheap, be adaptable: Design function objects to be values that are cheap to copy. Where possible, make them adaptable by inheriting from unary_- or binary_function.

Discussion Function objects are modeled on function pointers. Like function pointers, the convention is to pass them by value. All of the standard algorithms pass objects by value, and your algorithms should too. For example: template Function for_each( InputIter first, InputIter last, Function f ); Therefore, function objects must be cheap to copy and monomorphic (immune to slicing, so avoid virtual functions; see Item 54). But large and/or polymorphic objects are useful, and using them is okay; just hide the size and richness using the Pimpl idiom (see Item 43), which leaves the outer class as the required cheap-tocopy monomorphic type that still accesses rich state. The outer class should: x Be adaptable: Inherit from unary_function or binary_function. (See below.) x Have a Pimpl: Store a pointer (e.g., shared_ptr) to a large/rich implementation. x Have the function call operator(s): Pass these through to the implementation object. That should be all that’s needed in the outer class, other than possibly providing non-default versions of construction, assignment, and/or destruction. Function objects should be adaptable. The standard binders and adapters rely on certain typedefs, which are provided most conveniently when your function object derives from unary_function or binary_function. Instantiate unary_function or binary_function with the same types as your operator() takes and returns, except that for each non-pointer type strip off any top-level consts and &s from the type. Avoid providing multiple operator() functions, because that makes adaptability difficult. It’s usually impossible to provide the right adaptability typedefs because the same typedef would have different values for different operator() functions. Not all function objects are predicates. Predicates are a subset of function objects. (See Item 87.)

References [Allison98] §15, §C • [Austern99] §4, §8, §15 • [Gamma95] Bridge • [Josuttis99] §8.2.4 • [Koenig97] §21, §29 • [Meyers97] §34 • [Meyers01] §38, §40, §46 • [Musser01] §2.4, §8, §23 • [Sutter00] §26-30 • [Vandevoorde03] §22

Type Safety Trying to outsmart a compiler defeats much of the purpose of using one. —Brian Kernighan & P.J. Plauger

If you lie to the compiler, it will get its revenge. —Henry Spencer

There will always be things we wish to say in our programs that in all known languages can only be said poorly. —Alan Perlis

Last, but certainly not least, we will consider type correctness—a very important property of a program that you should strive to preserve at all times. Theoretically, a type-correct function can never access untyped memory or return forged values. Practically, if your code maintains type soundness, it avoids a large category of nasty errors ranging from nonportability to corrupting memory to creating bogus values to exhibiting undefined behavior. The basic idea underpinning how to maintain type soundness is to always read bits in the format in which they were written. Sometimes, C++ makes it easy to break this rule, and the following Items detail how to avoid such mistakes. Our vote for the most valuable Item in this section goes to Item 91: Rely on types, not on representations. The type system is your friend and your staunchest ally; enlist its help, and try never to abuse its trust.

173

174

C++ Coding Standards

90. Avoid type switching; prefer polymorphism. Summary Switch off: Avoid switching on the type of an object to customize behavior. Use templates and virtual functions to let types (not their calling code) decide their behavior.

Discussion Type switching to customize behavior is brittle, error-prone, unsafe, and a clear sign of attempting to write C or Fortran code in C++. It is a rigid technique that forces you to go back and do surgery on existing code whenever you want to add new features. It is also unsafe because the compiler will not tell you if you forget to modify all of the switches when you add a type. Ideally, adding new features to a program equates to adding more new code (see Item 37). In reality, we know that that’s not always true—oftentimes, in addition to writing new code, we need to go back and modify some existing code. Changing working code is undesirable and should be minimized, however, for two reasons: First, it might break existing functionality. Second, it doesn’t scale well as the system grows and more features are added, because the number of “maintenance knots” that you need to go back and change increases as well. This observation led to the Open-Closed principle that states: An entity (e.g., class or module) should be open for extension but closed for modification. (See [Martin96c] and [Meyer00].) How can we write code that can be easily extended without modifying it? Use polymorphism by writing code in terms of abstractions (see also Item 36) and then adding various implementations of those abstractions as you add functionality. Templates and virtual function calls form a dependency shield between the code using the abstractions and the code implementing them (see Item 64). Of course, managing dependencies this way depends on finding the right abstractions. If the abstractions are imperfect, adding new functionality will require changing the interface (not just adding new implementations of the interface), which usually requires changes to existing code. But abstractions are called “abstractions” for a reason—they are supposed to be much more stable than the “details,” that is, the abstractions’ possible implementations. Contrast that with code that uses few or no abstractions, but instead traffics directly in concrete types and their specific operations. That code is “detailed” already—in fact, it is swimming in a sea of details, a sea in which it is doomed soon to drown.

Examples Example: Drawing shapes. The classic example is drawing objects. A typical C-style, switch-on-type solution would define an enumerated member variable id_ for each

Type Safety

175

shape that stores the type of that shape: rectangle, circle, and so on. Drawing code looks up the type and performs specific tasks: class Shape { // … enum { RECTANGLE, TRIANGLE, CIRCLE } id_; void Draw() const { switch( id_ ) { case RECTANGLE: // … rectangle drawing code … break;

// bad

case TRIANGLE: // … triangle drawing code … break; case CIRCLE: // … circle drawing code … break;

};

}

default: // bad assert( !”Oops, forgot to update this switch when adding a new Shape” ); break; }

Such code creaks under its own weight, fragility, rigidity, and complexity. In particular, it suffers from the dreaded transitive cyclic dependency mentioned in Item 22. The default branch is symptomatic of “don’t know what to do with this type” syndrome. Contrast that with an implementation that you could pull from any OO text: class Shape { // … virtual void Draw() const = 0; };

// let each derived class implement it

Alternatively (or in addition), consider this implementation that follows the advice to make decisions at compile time where possible (see Item 64): template void Draw( const S& shape ) { shape.Draw(); };

// might or might not be virtual; // see Item 64

Now the responsibility of drawing each geometric figure goes to the figure implementation itself—and there’s no more “can’t handle this type” syndrome anymore.

References [Dewhurst03] §69, §96 • [Martin96c] • [Meyer00] • [Stroustrup00] §12.2.5 • [Sutter04] §36

176

C++ Coding Standards

91. Rely on types, not on representations. Summary Don’t try to X-ray objects (see Item 96): Don’t make assumptions about how objects are exactly represented in memory. Instead, let types decide how their objects are written to and read from memory.

Discussion The C++ Standard makes few guarantees about how types are represented in memory: x Base two is guaranteed for integral numbers. x Two’s complement is guaranteed for negative integral numbers. x Plain Old Data (POD) types have C-compatible layout: Member variables are stored in their order of declaration. x int holds at least 16 bits. In particular, the following may be common but are not guaranteed on all current architectures, and in particular are liable to be broken on newer architectures: x int is not exactly 32 bits, nor any particular fixed size. x Pointers and ints don’t always have the same size and can’t always be freely cast to one another. x Class layout does not always store bases and members in declaration order. x There can be gaps between the members of a class (even a POD) for alignment. x offsetof only works for PODs, not all classes (but compilers might not emit errors). x A class might have hidden fields. x Pointers might not look like integers at all. If two pointers are ordered, and you cast them to integral values, the resulting values might not be ordered the same way. x You can’t portably assume anything about the placement in memory of automatic variables, or about the direction in which the stack grows. x Pointers to functions might have a different size than void*, even though some APIs force you to assume that their sizes are the same. x You can’t always write just any object at just any memory address, even if you have enough space, due to alignment issues. Just define types appropriately, then read and write data using those types instead of thinking bits and words and addresses. The C++ memory model ensures efficient execution without forcing you to rely on manipulating representation. So don’t.

References [Dewhurst03] §95

Type Safety

177

92. Avoid using reinterpret_cast. Summary Lies have short legs (German and Romanian proverb): Don’t try to use reinterpret_cast to force the compiler to reinterpret the bits of an object of one type as being the bits of an object of a different type. That’s the opposite of maintaining type safety, and reinterpret_cast isn’t even guaranteed to do that or anything else in particular.

Discussion Recall: If you lie to the compiler, it will get its revenge. —Henry Spencer reinterpret_cast reflects the strongest assumption a programmer can make about object representation, namely that the programmer knows better than the compiler—to the point of being determined to pick an argument with the compiler’s carefully maintained type information. Compilers will shut up if you tell them to, but use of force should be a last resort. Avoid assuming how data is represented, because such assumptions dramatically affect the safety and reliability of your code. Besides, the reality is that reinterpret_cast’s effects are worse than reinterpreting objects’ bit patterns (which would be bad enough). Except that some conversions are guaranteed to be reversible, its effects are actually implementation-defined, so you don’t know if it will do even that. It is unreliable and nonportable.

Exceptions Some low-level system-specific programming might force you to use reinterpret_cast to stream bits in and out some port, or to transform integers in addresses. Use unsafe casting as rarely as you can and only in well-hidden functions that abstract it away, so as to make your code ready for porting with minimal changes. If you need to cast among unrelated pointer types, prefer casting via void* instead of using reinterpret_cast directly. That is, instead of T1* p1 = … ; T2* p2 = reinterpret_cast( p1 ); write T1* p1 = … ; void* pV = p1; T2* p2 = static_cast( pV );

References [C++03] §5.2.10(3) • [Dewhurst03] §39 • [Stroustrup00] §5.6

178

C++ Coding Standards

93. Avoid using static_cast on pointers. Summary Pointers to dynamic objects don’t static_cast: Safe alternatives range from using dynamic_cast to refactoring to redesigning.

Discussion Consider replacing uses of static_cast with its more powerful relative dynamic_cast, and then you won’t have to remember when static_cast is safe and when it’s dangerous. Although dynamic_cast can be slightly less efficient, it also detects illegal casting (and don’t forget Item 8). Using static_cast instead of dynamic_cast is like eliminating the stairs night-light, risking a broken leg to save 90 cents a year. Prefer to design away downcasting: Refactor or redesign your code so that it isn’t needed. If you see that you are passing a Base to a function that really needs a Derived to do its work, examine the chain of calls to identify where the needed type information was lost; often, changing a couple of prototypes leads to an excellent solution that also clarifies the type information flow to you. Excessive downcasts might indicate that the base class interface is too sparse. This can lead to designs that define more functionality in derived classes, and then downcast every time the extended interface is needed. The one good solution is to redesign the base interface to provide more functionality. If, and only if, the overhead of the dynamic_cast actually matters (see Item 8), consider defining your own cast that uses dynamic_cast during debugging and static_cast in the “all speed no guarantees” mode (see [Stroustrup00]): template To checked_cast( From* from ) { assert( dynamic_cast(from) == static_cast(from) && ”checked_cast failed” ); return static_cast( from ); } template To checked_cast( From& from ) { typedef tr1::remove_reference::type* ToPtr; // leverage [C++TR104] assert( dynamic_cast(&from) == static_cast(&from) && ”checked_cast failed” ); return static_cast( from ); } This little duo of functions (one each needed for pointers and references) simply tests whether the two casts agree. We leave it up to you to customize checked_cast for your needs, or to use one provided by a library.

References [Dewhurst03] §29, §35, §41 • [Meyers97] §39 • [Stroustrup00] §13.6.2 • [Sutter00] §44

Type Safety

179

94. Avoid casting away const. Summary Some fibs are punishable: Casting away const sometimes results in undefined behavior, and it is a staple of poor programming style even when legal.

Discussion Once you go const, you (should) never go back. If you cast away const on an object whose original definition really did define it to be const, all bets are off and you are in undefined behavior land. For example, compilers can (and do) put constant data into ROM or write-protected RAM pages. Casting away const from such a truly const object is a punishable lie, and often manifests as a memory fault. Even when it doesn’t crash your program, casting away const is a broken promise and doesn’t do what many expect. For example, this doesn’t allocate a variable-sized array: void Foolish( unsigned int n ) { const unsigned int size = 1; const_cast(size) = n; char buffer[size]; // … }

// bad: don’t do this // will always have size 1

C++ has one implicit const_cast, the “conversion of death” from a string literal to char*: char* weird = “Trick or treat?”; The compiler performs a silent const_cast from const char[16] to char*. This was allowed for compatibility with C APIs, but it’s a hole in C++’s type system. String literals are ROM-able, and trying to modify the string is liable to cause a memory fault.

Exceptions Casting away const can be necessary to call const-incorrect APIs (see Item 15). It is also useful when a function that must take and return the same kind of reference has const and non-const overloads, implemented by having one call the other: const Object& f( const Object& ); Object& f( Object& obj ) { const Object& ref = obj; return const_cast( f(ref) ); }

References [Dewhurst03] §32, §40 • [Sutter00] §44

// have to const_cast the return type

180

C++ Coding Standards

95. Don’t use C-style casts. Summary Age doesn’t always imply wisdom: C-style casts have different (and often dangerous) semantics depending on context, all disguised behind a single syntax. Replacing C-style casts with C++-style casts helps guard against unexpected errors.

Discussion One problem with C-style casts is that they provide one syntax to do subtly different things, depending on such vagaries as the files that you #include. The C++ casts, while retaining some of the dangers inherent in casts, avoid such ambiguities, clearly document their intent, are easy to search for, take longer to write (which makes one think twice)—and don’t silently inject evil reinterpret_casts (see Item 92). Consider the following code, where Derived inherits from Base: extern void Fun( Derived* ); void Gun( Base* pb ) { // let’s assume Gun knows for sure pb actually points to a Derived // and wants to forward to Fun Derived* pd = (Derived*)pb; // bad: C-style cast Fun( pd ); } If Gun has access to the definition of Derived (say by including “derived.h”), the compiler will have the necessary object layout information to make any needed pointer adjustments when casting from Base to Derived. But say Gun’s author forgets to #include the appropriate definition file, and Gun only sees a forward declaration of class Derived;. In that case, the compiler will just assume that Base and Derived are unrelated types, and will reinterpret the bits that form Base* as a Derived*, without making any necessary adjustments dictated by object layout! In short, if you forget to #include the definition, your code crashes mysteriously, even though it compiles without errors. Avoid the problem this way: extern void Fun( Derived* ); void Gun( Base* pb ) { // if we know for sure that pb actually points to a Derived, use: Derived* pd = static_cast(pb); // good: C++-style cast // otherwise: = dynamic_cast(pb); // good: C++-style cast Fun(pd); }

Type Safety

181

Now, if the compiler doesn’t have enough static information about the relationship between Base and Derived, it will issue an error instead of automatically performing a bitwise (and potentially lethal) reinterpret_cast (see Item 92). The C++-style casts can protect the correctness of your code during system evolution as well. Say you have an Employee-rooted hierarchy and you need to define a unique employee ID for each Employee. You could define the ID to be a pointer to the Employee itself. Pointers uniquely identify the objects they point to and can be compared for equality, which is exactly what’s needed. So you write: typedef Employee* EmployeeID; Employee& Fetch( EmployeeID id ) { return *id; } Say you code a fraction of the system with this design. Later on, it turns out that you need to save your records to a relational database. Clearly, saving pointers is not something that you’d want to do. So, you change the design such that each employee has a unique integral identifier. Then, the integral IDs can be persisted in the database, and a hash table maps the IDs to Employee objects. Now the typedef is: typedef int EmployeeID; Employee& Fetch( EmployeeID id ) { return employeeTable_.lookup(id); } That is a valid design, and you’d expect that all misuses of EmployeeID would be flagged as compile-time errors. And they will, except for this little obscure code: void TooCoolToUseNewCasts( EmployeeID id ) { Secretary* pSecretary = (Secretary*)id; // … }

// bad: C-style cast

With the old typedef, the C-style cast performed a static_cast; with the new one, it performs a reinterpret_cast against some integer, firmly planting the code in the scary realm of undefined behavior (see Item 92). C++-style casts are also easy to search for with automatic tools such as grep. (No grep regular expression can catch the C cast syntax.) Because casts are very dangerous (especially static_cast of pointers and reinterpret_cast; see Item 92), using automated tools to keep track of them is always a good idea.

References [Dewhurst03] §40 • [Meyers96] §2 • [Stroustrup00] §15.4.5 • [Sutter00] §44

182

C++ Coding Standards

96. Don’t memcpy or memcmp non-PODs. Summary Don’t try to X-ray objects (see Item 91): Don’t use memcpy and memcmp to copy or compare anything more structured than raw memory.

Discussion memcpy and memcmp violate the type system. Using memcpy to copy objects is like making money using a photocopier. Using memcmp to compare objects is like comparing leopards by counting their spots. The tools and methods might appear to do the job, but they are too coarse to do it acceptably. C++ objects are all about information hiding (arguably the most profitable principle in software engineering; see Item 11): Objects hide data (see Item 41) and devise precise abstractions for copying that data through constructors and assignment operators (see Items 52 through 55). Bulldozing over all that with memcpy is a serious violation of information hiding, and often leads to memory and resource leaks (at best), crashes (worse), or undefined behavior (worst). For example: {

shared_ptr p1( new int ), p2( new int ); memcpy( &p1, &p2, sizeof(p1) ); } // memory leak: p2’s int is never deleted // memory corruption: p1’s int is deleted twice

// create two ints on the heap // bad: a heinous crime

Abusing memcpy can affect aspects as fundamental as the type and the identity of an object. Compilers often embed hidden data inside polymorphic objects (the socalled virtual table pointer, or vptr) that give the object its run-time identity. In the case of multiple inheritance, several such vptrs can coexist at various offsets inside the object, and most implementations add yet more internal pointers when using virtual inheritance. During normal use, the compiler takes care of managing all of these hidden fields; memcpy can only wreak havoc. Similarly, memcmp is an inappropriate tool for comparing anything more elaborate than bits. Sometimes, it does too little (e.g., comparing C-style strings is not the same as comparing the pointers with which the strings are implemented). And sometimes, paradoxically, memcmp does too much (e.g., memcmp will needlessly compare bytes that are not part of an object’s state, such as padding inserted by the compiler for alignment purposes). In both cases, the comparison’s result will be wrong.

References [Dewhurst03] §50 • [Stroustrup94] §11.4.4

Type Safety

183

97. Don’t use unions to reinterpret representation. Summary A deceit is still a lie: Unions can be abused into obtaining a “cast without a cast” by writing one member and reading another. This is more insidious and even less predictable than reinterpret_cast (see Item 92).

Discussion Don’t read a field of a union unless it was the field that was last written. Reading a different field of a union than the field that was last written has undefined behavior, and is even worse than doing a reinterpret_cast (see Item 92); at least with the latter the compiler has the fighting chance to warn and repel an “impossible reinterpretation” such as pointer to char. When abusing a union, no reinterpretation of bits will ever yield a compile-time error or a reliable result. Consider this code that is intended to deposit a value of one type (char*) and extract the bits of that value as a different type (long): union { long intValue_; char* pointerValue_; }; pointerValue_ = somePointer; long int gotcha = intValue_; This has two problems: x It assumes too much: It assumes that sizeof(long) and sizeof(char*) are equal, and that the bit representations are identical. These things are not true on all implementations (see Item 91). x It obscures your intent for both human readers and compilers: Playing union games makes it harder for compilers to catch genuine type errors, and for humans to spot logical errors, than even the infamous reinterpret_cast (see Item 92).

Exceptions If two POD structs are members of a union and start with the same field types, it is legal to write one such matching field and read another.

References [Alexandrescu02b] • [Stroustrup00] §C.8.2 • [Sutter04] §36

184

C++ Coding Standards

98. Don’t use varargs (ellipsis). Summary Ellipses cause collapses: The ellipsis is a dangerous carryover from C. Avoid varargs, and use higher-level C++ constructs and libraries instead.

Discussion Functions taking a variable number of arguments are a nice commodity, but C-style varargs aren’t the way to get them. Varargs have many serious shortcomings: x Lack of type safety: Essentially, the ellipsis tells the compiler: “Turn all checking off. I’ll take over from here and start reinterpret_casting.” (See Item 92.) x Tight coupling and required manual cooperation between caller and callee: The language’s type checking has been disabled, so the call site must use alternate ways to communicate the types of the arguments being passed. Such protocols (e.g., printf’s format string) are notoriously error-prone and unsafe because they cannot be fully checked or enforced by either the caller or the callee. (See Item 99.) x Undefined behavior for objects of class type: Passing anything but primitive and POD (plain old data) types via varargs has undefined behavior in C++. Unfortunately, most compilers don’t give a warning when you do it. x Unknown number of arguments: For even the simplest variable-arguments function (e.g., min) with a variable number of arguments of known type (e.g., int), you still need to have a protocol for figuring out the number of arguments. (Ironically, this is a good thing because it further discourages using varargs.) Avoid using varargs in your functions’ signatures. Avoid calling functions with varargs in their own signatures, including legacy functions and standard C library functions such as sprintf. Admittedly, calls to sprintf can often look more compact and easier to read than equivalent calls using stringstream formatting and operator