Overloading in C ++. Part II Operator Overload



We continue the series "C ++, digging in depth." The purpose of this series is to tell you as much as possible about the various features of the language, possibly quite special. This article is about operator overloading. Particular attention is paid to the use of overloaded operators in the standard library. This is the second article in the series, the first devoted to overloading functions and templates, is here . The next article will focus on overloading memory management statements.



Table of contents


Table of contents

  
  1.
    1.1.
    1.2.
    1.3. ,
    1.4.
    1.5.
      1.5.1.
      1.5.2.
      1.5.3.
  2.
    2.1.
    2.2.
      2.2.1.
      2.2.2.
      2.2.3.
      2.2.4.
    2.3.
    2.4.
    2.5.
    2.6.
  3.
    3.1. ->
    3.2. *
    3.3. []
      3.3.1.
    3.4. ()
      3.4.1. -
      3.4.2.
      3.4.3. -
      3.4.4.
      3.4.5.
      3.4.6.
      3.4.7.
    3.5.
    3.6.
    3.7. ,
    3.8. << >>
    3.9.
    3.10. !
  4.
  
     .
     . - C-
  




Introduction


Operator overloading is the ability to apply built-in language operators to different types, including user types. In fact, this is a fairly old idea. Already in the first programming languages, symbols of arithmetic operations: +.-, etc. were used for operations on integers and real numbers, despite the fact that they have different sizes and different internal representations and, accordingly, these operations are implemented in different ways. With the advent of object-oriented languages, this idea was further developed. If operations on user-defined types have similar semantics to operations on built-in types, then why not use the syntax of built-in operators. This can increase the readability of the code, make it more concise and expressive, simplify the writing of generalized code. In C ++, operator overloading has serious support and is actively used in the standard library.



1. General issues of operator overload



1.1. Overloaded Operators


C++17 : +, -, *, /, %, ^, &, |, ~, !, ,, =, <, >, <=, >=, ++, โ€“-, <<, >>, ==, !=, &&, ||, +=, -=, /=, %=, ^=, &=, |=, *=, <<=, >>=, [], (), ->, ->*, new, new[], delete, delete[].
( , C++98.) , , , . , (), . +, -, *, &, ++, โ€“- ( ) โ€” , , 6 .



1.2.


, . โ€” + += std::basic_string<>. std::filesystem::path (C++17). / /= . , . .


, . โ€” << . , , , .


std::out<<c?x:y;



(std::out<<c)?x:y;



std::out<<(c?x:y);

.


std::out void*, - . -, , . += , , , , .



1.3. ,


: , (), &&, ||. , ( ), (short-circuit evaluation), , . ( , , , && , false, || , true.)


& ( ). & , . ++11 ( ) std::addressof(), & .



1.4.


. , . , , . , , , . , , . a=b=c. bool . +, -, ~ . , . , , ( . [Sutter1]). , , . .



1.5.



1.5.1.


: - (-) . - โ€” =, ->, [], (). .


, - - operator@, @ () . , . () .


, (-) , operator@, @ () . , , . โ€” , ( ), , . , , . :


namespace N
{
    class X
    {
    // ...
        X operator+() const;           //  
        X operator+(const X& x) const; //  
        void operator()(int x, int y); //  
        char operator[](int i);        // 
    };
    X operator-(const X& x);             //  
    X operator-(const X& x, const X& y); //  
}

, , -, .



1.5.2.


(): . .


( , N):


N::X x, y;
//  
N::X z = x + y;
N::X v = x โ€“ y;
N::X w = +x;
N::X u = -x;
x(1,2);
char p = x[4];
//  
N::X z = x.operator+(y);
N::X v = operator-(x, y);
N::X w = x.operator+();
N::X u = operator-(x);
x.operator()(1,2);
char p = x.operator[](4);

, , , . , , . ( ) , . ( โ€” .) -> , , .


, , (argument depended lookup, ADL), , , , , , . , ADL .



1.5.3.


, , - . . , , . . , . , .



2.



2.1.


. โ€” const ( -), ( ). () .


std::string + : const std::string&, const char*.


3.4.2 ().


() , .



2.2.


, , , .



2.2.1.


โ€” . , x@y, y@x . , - . + std::string, const char*.



2.2.2.


-. (, -, .) . -, , , -, . . 3.8.



2.2.3.


โ€” . X , , X. , . , -, . .


int i = 42;
std::reference_wrapper<int> rwi(i);
std::cout << rwi << '\n'; // : 42

std::reference_wrapper<int>, int&, . , , . , 2.3.



2.2.4.


, -, . 2.6.



2.3.


, , . , .


class X
{
// ...
    friend X operator+(const X& x, const X& y) //  
    {
    // ...
    }
};

. , , , . , . , [Meyers1]. + friend, - .


// rational number ( )
template<typename T>
class Rational 
{
    T num; // numerator ()
    T den; // denominator ()
public:
    Rational(T n = 0, T d = 1) : num(n), den(d) {/* ... */}

    T Num() const { return num; }
    T Den() const { return den; }

    friend const Rational operator+(
        const Rational& x, const Rational& y)
    {
        return Rational(
                x.num * y.den + y.num * x.den,
                x.den * y.den);
    }
};
template<typename T>
const Rational<T>operator-(
    const Rational<T>& x, const Rational<T>& y)
{
    return Rational<T>(
        x.Num() * y.Den() - y.Num() * x.Den(), 
        x.Den() * y.Den());
}

+ . , T Rational, . :


Rational<int> r1(1, 2), r2(31, 64);
Rational<int> r3 = r1 + r2; // Rational + Rational
Rational<int> r4 = r1 + 3;  // Rational + int
Rational<int> r5 = 4 + r2;  // int + Rational

Rational int. int int Rational, .


-.


Rational<int> r6 = r1 - 3; // Rational - int
Rational<int> r7 = 4 - r2; // int - Rational

, int Rational . , , -:


template<typename T>
const Rational<T> operator-(const Rational<T>& x, T y)
{
    return operator-(x, Rational<T>(y));
}

template<typename T>
const Rational<T> operator-(T x, const Rational<T>& y)
{
    return operator-(Rational<T>(x), y);
}

. [Meyers1].



2.4.


, , (computational constructor). (return value optimization, RVO). . [Dewhurst].



2.5.


-, . , , ( NVI โ€“ non virtual interface). ยซยป . . , , *a+*b. , ( +), , ( Visitor). , , . [Dewhurst]. . , , โ€” .



2.6.


, , . :


enum class Color { Begin, Red = Begin, Green, Blue, End};
//  
Color& operator++(Color& col) { return (Color&)(++(int&)col); }

:


void Foo(Color col);
// ...
for (Color col = Color::Begin; col < Color::End; ++col)
{
    Foo(col);
}



Color operator*(Color col) { return col; }

:


std::for_each(Color::Begin, Color::End, Foo);

. :


struct Colors
{
    Color begin() const { return Color::Begin; }
    Color end() const { return Color::End; }
};

for:


for (auto col : Colors())
{
    Foo(col);
}


3.


, .



3.1. ->


- ( ). (, ), , ->. ยซยป โ€” . :


class X
{
// ...
    void Foo();
};
class XPtr
{
// ...
    X* operator->() const;
};
// ...
X x;
x->Foo();              //  
x.operator->()->Foo(); //  

-> .



3.2. *


->. , , ->. -.


* .



3.3. []


, , , -, . , , , . ยซยป , , . . , , , , &[i], . , , .


โ€” .


T& operator[](int ind);
const T& operator[](int ind) const;

, -.


std::vector<>, std::array<>, std::basic_string<>, std::deque<> std::map<>, std::unordered_map<>. std::unique_ptr<> .



3.3.1.


C++ , a[i,j] , ยซ ยป, a[i][j]. -. .


template<typename T>
class Matrix
{
public:
    Matrix(int rowCount, int colCount);

    class RowProxy;
    RowProxy operator[](int i) const;

    class RowProxy
    {
    public:
        T& operator[](int j);
        const T& operator[](int j) const;
        // ...
    };
    // ...
};
// ...
Matrix<double> mtx(5, 6);
double s = mtx[1][2];
mtx[2][3] = 3.14;


3.4. ()


-. , . , (), , . C++ . C++ . , , : , , -, , . , . , , ( ), () -. โ€” std::for_each(), .



3.4.1. -


C++ ( ). . - ยซ ยป.



3.4.2.


(), . . .



3.4.3. -


( std::unordered_set<>, std::unordered_multiset<>, std::unordered_map<>, std::unordered_multimap<>) , - . -. () - std::size_t. , . std::hash<>, . , . , , -. .


  1. .
  2. .

C- .



3.4.4.


, . . () , , bool. , . std::less<> std::equal_to<>, . <, ==.


std::less<> std::set<>, std::multiset<>, std::map<>, std::multimap<>, std::priority_queue<>.


std::equal_to<> std::unordered_set<>, std::unordered_multiset<>, std::unordered_map<>, std::unordered_multimap<>.


, .


  1. , . , , , , ( ).
  2. , .
  3. .

C- .



3.4.5.


, . , (deleter). () . std::unique_ptr<> std::default_delete<>, delete ( delete[] ). std::shared_ptr<> delete. , . .


  1. std::unique_ptr<> -.
  2. .

- std::shared_ptr<>, std::shared_ptr<>.



3.4.6.


, , . , ( ), .


If the necessary functional object is not specified, the operator is <used by default in an algorithm std::lexicographical_compare()that compares ranges, in minimum / maximum search algorithms ( min_element(), etc), in algorithms related to sorting and sorted data ( std::sort(), etc), in pyramid-related algorithms ( std::make_heap(), etc).

The operator is ==used by default in an algorithm std::equal()that compares ranges, in an algorithm std::count()that counts the number of specified elements, in search algorithms ( std::find(), etc), in algorithms std::replace()and std::remove()that modify a range.


The operator is +used by default in the algorithm accumulate(). (For details, see Appendix A.)


, .


  1. , . , .
  2. , . ( -.)

C- .



3.4.7.


C++11 . () . , - . .


#include <functional>

int Foo(const char* s) { return *s; }

struct X
{
    int operator() const (const char* s) { return *s; }
};

std::function<int(const char*)>
    f1 = Foo,
    f2 = X(),
    f3 = [](const char* s) { return *s; };

int r1 = f1("1"),
    r2 = f2("2"),
    r3 = f3("3");


3.5.


. bool.


< ==, . . (. [Josuttis]). < : ( x<y true, y<x false), ( x<y y<z, x<z), (x<x false), ( !(x<y) && !(y<x) !(y<z) && !(z<y), !(x<z) && !(z<x)). == : ( x==y, y==x), ( x==y y==z, x==z), (x==x true). , . , .

. . < ==, !. , . std::rel_ops <=, >, >=, !=. ( <utility>.) .


#include <utility>

class X { /* ... */ };
//  
bool operator==(const X& lh, const X& rh);
bool operator<(const X& lh, const X& rh);
//  
bool operator<=(const X& lh, const X& rh)
{
    return std::rel_ops::operator<=(lh, rh);
}
//   

, std::rel_ops , using-:


using namespace std::rel_ops;

โ€” <, <=, >, >=, ==, !=, , : std::thread::id, std::type_index, std::monostate. , , . .


== != std::error_code, std::bitset. , < .



3.6.


, + +=. , โ€” -. , +, -, -. .


class X
{
// ...
    const X operator-() const; //  
    X& operator+=(const X& x); //  
};
const X operator+(const X& x, const X& y); //  

+, - ( ) . . , , . -, *this. + += , 1.4.1, , X , const .


. C-, . (. 2.2).


std::complex<>. +, +=, -, -= . std::basic_string<> + += . std::filesystem::path (C++17). / /= . , . .




3.7. ,


. , . - . .


class Iter
{
public:
    Iter& operator++() //  
    {
        //  
        return *this;
    }

    const Iter operator++(int) //  
    {
        Iter it(*this);
        ++*this;
        return it;
    }
    // ...
};

, , , . 1.4.


, .



3.8. << >>


( ). , : , , . .


#include <iostream>

struct Point
{
    int X;
    int Y;
};
std::ostream& operator<<(std::ostream& strm, const Point& p)
{
    strm << '[' << p.X << ',' << p.Y << ']';
    return strm;
}

โ€” , , . void*, - . . 1.2.



3.9.


, -, . , , , . / . , . โ€” . ยซ ยป, , ( ), , .


โ€” , .



class X
{
public:
    X(const X& src);     //  
    X(X&& src) noexcept; //  

    X& operator=X(const X& src);     //   
    X& operator=X(X&& src) noexcept; //   
// ...
};

, , . - noexcept.


. "=default".


class X
{
public:
    X& operator=X(const X& src) = default;
    X& operator=X(X&& src) = default; 
// ...
};

. , , .


class X
{
public:
    X& operator=X(const X& src) = delete;
    X& operator=X(X&& src) = delete; 
// ...
};

. , *this. , a=b=c. โ€” ยซ ยป. - , .


class X
{
public:
    void Swap(X& src) noexcept;     //  
    X(const X& src);                //  
    X(X&& src) noexcept;            //  
    X& operator=X(const X& src);
    X& operator=X(X&& src) noexcept;
// ...
};

:


X& X::operator=X(const X& src)
{
    X tmp(src); // 
    Swap(tmp);
    return *this;
}
X& X::operator=X(X&& src) noexcept
{
    X tmp(std::move(src)); // 
    Swap(tmp);
    return *this;
}

, .
: , , ( ).



ยซ ยป , .


X& X::operator=X(const X& src)
{
    if (this != std::addressof(src))
    {
        // ...
    }
    return *this;
}

, , . ยซ ยป , , , , .


, , .


X& X::operator=X(const X& src)
{
    if (this != std::addressof(src))
    {
        this->~X();
        new (this)(src);
    }
    return *this;
}

this, new , this, . , , , . , , this, , . . , X - X . this->~X() , . .


( ) , /. ( .) , . .



3.10. !


, . true, (ยซยป, ยซยป). . explicit bool, , true, .


explicit operator bool() const noexcept;


4.


. , .


.





.


std::variant<> (C++17). std::visit() , () std::variant<>, . std::visit() () , std::variant<>. .


using IntStr = std::variant<int, std::string>;

struct Visitor
{
    void operator()(int x) const
    { 
        std::cout << "int, val=" << x << '\n';
    }
    void operator()(const std::string& x) const
    {
        std::cout << "string, val=" << x << '\n';
    }
};
// ...
IntStr a(42), b("meow");
Visitor v;
std::visit(v, a); // : int, val=42
std::visit(v, b); // : string, val=meow

std::reduce() (C++17). std::accumulate(). std::accumulate().


template<class InputIt, class T, class BinOper>
T accumulate(InputIt first, InputIt last, T init, BinOper oper);

BinOper โ€” ,


T f(T t, S s);

T โ€” , S โ€” , T. , , T S , . , , . init. , C++17 std::reduce(), , .


template<class ExecutionPolicy,
    class InputIt, class T, class BinOper>
T reduce(ExecutionPolicy&& policy,
    InputIt first, InputIt last, T init, BinOper oper);

BinOper std::accumulate() โ€” , BinOper :


T f(T t, S s);
T f(S s1, S s2);
T f(T t1, T t2);


. - C-


C- โ€” , โ€” T* const T*, T (char, wchar_t, etc). std::hash<>, std::less<> std::equal_to<> , , . .


#include <functional>

template <class T>
inline void hash_combine(std::size_t& seed, const T& v)
{
    std::hash<T> hasher;
    seed ^= hasher(v) + 0x9e3779b9 + 
        (seed << 6) + (seed >> 2);
}

#include <cstring>

namespace std
{
    template <>
    struct hash<const char*>
    {
        size_t operator()(const char* str) const
        {
            std::size_t hash = 0;
            for (; *str; ++str)
            {
                hash_combine(hash, *str);
            }
            return hash;
        }
    };

    template <>
    struct equal_to<const char*>
    {
        bool operator()(const char* x, const char* y) const
        {
            return strcmp(x, y) == 0;
        }
    };

    template <>
    struct less<const char*>
    {
        bool operator()(const char* x, const char* y) const
        {
            return strcmp(x, y) < 0;
        }
    };
} // namespace std

hash_combine() โ€” Boost. -.


, , C- - .


#include <cstring>
const char* cc[] = { "one", "two", "three", "four" };

std::sort(cc, cc + _countof(cc),
    [](const char* x, const char* y)
        { return std::strcmp(x, y) < 0; });



[Josuttis]
Josattis, Nikolai M. C ++ Standard Library: Reference Guide, 2nd ed .: Per. from English - M .: LLC โ€œI.D. Williams ", 2014.
[Dewhurst]
Dewhurst, Stefan K. Slippery places C ++. How to avoid problems when designing and compiling your programs .: Per. from English - M.: DMK Press, 2012.
[Meyers1]
Meyers, Scott. Effective use of C ++. 55 sure ways to improve the structure and code of your programs .: Per. from English - M.: DMK Press, 2014.
[Sutter1]
Sutter, Coat of Arms. The solution of complex problems in C ++ .: Per. from English - M: LLC โ€œI.D. Williams, 2015.


All Articles