Ch17: Operator Overloading

中文(简体)

1. What Overloading Buys, and What It Costs

An overloaded operator is a function whose name is punctuation. That is the entire feature. a + b is exactly operator+(a, b), a[i] is exactly a.operator[](i), and the compiler's job is nothing more than substituting the second spelling for the first (Ch17.1).

The buy is real: for a numeric type, a * b + c reads the way the mathematics reads, and no reader has to decode a name. For a smart handle, p->member lets a wrapper be used where a pointer was used.

The cost is also real, and it is not the cost you would guess. It is not the extra function call — that is inlined. The cost is that punctuation carries no information. + cannot tell the reader whether it concatenated two sequences, allocated, copied ten megabytes, failed, or was associative. A named function can. A named function can also be found by a text search, put in a debugger breakpoint list, and called from a language that has no operator+.

2. The Rule This Chapter Argues For

Prefer a named function. Overload an operator only when the operator's meaning is the meaning the type already has, and when the operation cannot fail, cannot allocate, and cannot surprise.

The canonical example is sequence concatenation. For two strings, a + b is a C++ habit inherited from 1998 that says nothing about what happens to the bytes: it produces a new string, which means it copies every byte of a and every byte of b, which means an allocation, which means an error path. concat(a, b) says "produce one sequence out of two", and a.append(b) says "extend a with b, no new value, no copy of a". Three different operations, three different costs, and with + you cannot tell which one you got. Chapter 17.2 measures the difference: the same 4,000-byte result costs 998,000 copied bytes with + and zero with append.

Note what this rule is not. It is not "never overload operators" — Ch17.3 shows a fixed-point type where + and * are the right answer, and Ch17.4 shows == and <=> done properly. It is "do not use punctuation to hide a decision the reader has to know about".

3. The Table, in One Screen

Operator Verdict Reason
+ - * / on a numeric value type yes the type is arithmetic; no allocation, no failure, total (Ch17.3)
==, <=> yes if the type has one obvious equality and one obvious order (Ch17.4)
=>>=-style compound assignment yes it is the primitive; + is defined in terms of it
[] yes, if it is cheap and cannot fail container index; the moment it can insert or throw it lies (Ch17.7)
*, -> yes, for handles a wrapper that behaves like a pointer
() yes, for callables it is the contract every algorithm that takes a callable relies on; make it static inline constexpr when the callable is stateless, and never use it as a "do everything" entry point (Ch17.7 §6)
+ on containing types no hides allocation, copying, and failure; use concat or append (Ch17.2)
, never a function call cannot sequence its arguments, and sequencing is the only thing the comma operator is for (Ch17.8)
& (unary, address-of) never it is how C has named an object for fifty years, and the repair cost the standard library std::addressof (Ch17.8 §3)
<<, >> for I/O never shifting is not printing; iostreams are bad and harmful, which is why fast_io has print and scan (Ch17.8)
operator T() only explicit operator bool invisible conversions at a distance; the implicit ones silently change what == and + mean (Ch17.6)
++, -- yes, prefix first the prefix form is the primitive; postfix is three lines and one copy (Ch17.5)
operator new / operator delete never Ch18.7 shows the whole bill

4. Contents

5. What You Need Before This Chapter