c++ - What are the basic rules and idioms for operator overloading? -
note: answers given in a specific order, since many users sort answers according votes, rather time given, here's index of answers in order in make sense:
- the general syntax of operator overloading in c++
- the 3 basic rules of operator overloading in c++
- the decision between member , non-member
- common operators overload
- assignment operator
- input , output operators
- function call operator
- comparison operators
- arithmetic operators
- array subscripting
- operators pointer-like types
- conversion operators
- overloading new , delete
(note: meant entry stack overflow's c++ faq. if want critique idea of providing faq in form, the posting on meta started this place that. answers question monitored in c++ chatroom, faq idea started out in first place, answer read came idea.)
common operators overload
most of work in overloading operators boiler-plate code. little wonder, since operators merely syntactic sugar, actual work done (and forwarded to) plain functions. important boiler-plate code right. if fail, either operator’s code won’t compile or users’ code won’t compile or users’ code behave surprisingly.
assignment operator
there's lot said assignment. however, of has been said in gman's famous copy-and-swap faq, i'll skip of here, listing perfect assignment operator reference:
x& x::operator=(x rhs) { swap(rhs); return *this; }
bitshift operators (used stream i/o)
the bitshift operators <<
, >>
, although still used in hardware interfacing bit-manipulation functions inherit c, have become more prevalent overloaded stream input , output operators in applications. guidance overloading bit-manipulation operators, see section below on binary arithmetic operators. implementing own custom format , parsing logic when object used iostreams, continue.
the stream operators, among commonly overloaded operators, binary infix operators syntax specifies no restriction on whether should members or non-members. since change left argument (they alter stream’s state), should, according rules of thumb, implemented members of left operand’s type. however, left operands streams standard library, , while of stream output , input operators defined standard library indeed defined members of stream classes, when implement output , input operations own types, cannot change standard library’s stream types. that’s why need implement these operators own types non-member functions. canonical forms of 2 these:
std::ostream& operator<<(std::ostream& os, const t& obj) { // write obj stream return os; } std::istream& operator>>(std::istream& is, t& obj) { // read obj stream if( /* no valid object of t found in stream */ ) is.setstate(std::ios::failbit); return is; }
when implementing operator>>
, manually setting stream’s state necessary when reading succeeded, result not expected.
function call operator
the function call operator, used create function objects, known functors, must defined member function, has implicit this
argument of member functions. other can overloaded take number of additional arguments, including zero.
throughout c++ standard library, function objects copied. own function objects should therefore cheap copy. if function object absolutely needs use data expensive copy, better store data elsewhere , have function object refer it.
comparison operators
the binary infix comparison operators should, according rules of thumb, implemented non-member functions1. unary prefix negation !
should (according same rules) implemented member function. (but not idea overload it.)
the standard library’s algorithms (e.g. std::sort()
) , types (e.g. std::map
) expect operator<
present. however, users of type expect other operators present, too, if define operator<
, sure follow third fundamental rule of operator overloading , define other boolean comparison operators. canonical way implement them this:
inline bool operator==(const x& lhs, const x& rhs){ /* actual comparison */ } inline bool operator!=(const x& lhs, const x& rhs){return !operator==(lhs,rhs);} inline bool operator< (const x& lhs, const x& rhs){ /* actual comparison */ } inline bool operator> (const x& lhs, const x& rhs){return operator< (rhs,lhs);} inline bool operator<=(const x& lhs, const x& rhs){return !operator> (lhs,rhs);} inline bool operator>=(const x& lhs, const x& rhs){return !operator< (lhs,rhs);}
the important thing note here 2 of these operators anything, others forwarding arguments either of these 2 actual work.
the syntax overloading remaining binary boolean operators (||
, &&
) follows rules of comparison operators. however, very unlikely find reasonable use case these2.
1 as rules of thumb, there might reasons break one, too. if so, not forget left-hand operand of binary comparison operators, member functions *this
, needs const
, too. comparison operator implemented member function have have signature:
bool operator<(const x& rhs) const { /* actual comparison *this */ }
(note const
@ end.)
2 it should noted built-in version of ||
, &&
use shortcut semantics. while user defined ones (because syntactic sugar method calls) not use shortcut semantics. user expect these operators have shortcut semantics, , code may depend on it, therefore highly advised never define them.
arithmetic operators
unary arithmetic operators
the unary increment , decrement operators come in both prefix , postfix flavor. tell 1 other, postfix variants take additional dummy int argument. if overload increment or decrement, sure implement both prefix , postfix versions. here canonical implementation of increment, decrement follows same rules:
class x { x& operator++() { // actual increment return *this; } x operator++(int) { x tmp(*this); operator++(); return tmp; } };
note postfix variant implemented in terms of prefix. note postfix copy.2
overloading unary minus , plus not common , best avoided. if needed, should overloaded member functions.
2 also note postfix variant more work , therefore less efficient use prefix variant. reason prefer prefix increment on postfix increment. while compilers can optimize away additional work of postfix increment built-in types, might not able same user-defined types (which innocently looking list iterator). once got used i++
, becomes hard remember ++i
instead when i
not of built-in type (plus you'd have change code when changing type), better make habit of using prefix increment, unless postfix explicitly needed.
binary arithmetic operators
for binary arithmetic operators, not forget obey third basic rule operator overloading: if provide +
, provide +=
, if provide -
, not omit -=
, etc. andrew koenig said have been first observe compound assignment operators can used base non-compound counterparts. is, operator +
implemented in terms of +=
, -
implemented in terms of -=
etc.
according our rules of thumb, +
, companions should non-members, while compound assignment counterparts (+=
etc.), changing left argument, should member. here exemplary code +=
, +
, other binary arithmetic operators should implemented in same way:
class x { x& operator+=(const x& rhs) { // actual addition of rhs *this return *this; } }; inline x operator+(x lhs, const x& rhs) { lhs += rhs; return lhs; }
operator+=
returns result per reference, while operator+
returns copy of result. of course, returning reference more efficient returning copy, in case of operator+
, there no way around copying. when write a + b
, expect result new value, why operator+
has return new value.3 note operator+
takes left operand by copy rather const reference. reason same reason giving operator=
taking argument per copy.
the bit manipulation operators ~
&
|
^
<<
>>
should implemented in same way arithmetic operators. however, (except overloading <<
, >>
output , input) there few reasonable use cases overloading these.
3 again, lesson taken a += b
is, in general, more efficient a + b
, should preferred if possible.
array subscripting
the array subscript operator binary operator must implemented class member. used container-like types allow access data elements key. canonical form of providing these this:
class x { value_type& operator[](index_type idx); const value_type& operator[](index_type idx) const; // ... };
unless not want users of class able change data elements returned operator[]
(in case can omit non-const variant), should provide both variants of operator.
if value_type known refer built-in type, const variant of operator should return copy instead of const reference.
operators pointer-like types
for defining own iterators or smart pointers, have overload unary prefix dereference operator *
, binary infix pointer member access operator ->
:
class my_ptr { value_type& operator*(); const value_type& operator*() const; value_type* operator->(); const value_type* operator->() const; };
note these, too, need both const , non-const version. ->
operator, if value_type
of class
(or struct
or union
) type, operator->()
called recursively, until operator->()
returns value of non-class type.
the unary address-of operator should never overloaded.
for operator->*()
see this question. it's used , ever overloaded. in fact, iterators not overload it.
continue conversion operators
Comments
Post a Comment