Ăśberladen in C ++. Teil II Ăśberlastung des Bedieners



Wir setzen die Serie "C ++, Graben in die Tiefe" fort. Der Zweck dieser Reihe ist es, so viel wie möglich über die verschiedenen Merkmale der Sprache zu erzählen, möglicherweise ganz besonders. In diesem Artikel geht es um das Überladen von Bedienern. Besonderes Augenmerk wird auf die Verwendung überladener Operatoren in der Standardbibliothek gelegt. Dies ist der zweite Artikel in der Serie ist die erste zu einer Überlastung Funktionen und Vorlagen gewidmet, hier . Der nächste Artikel befasst sich mit dem Überladen von Speicherverwaltungsanweisungen.



Inhaltsverzeichnis


Inhaltsverzeichnis

  
  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-
  




EinfĂĽhrung


Operatorüberladung ist die Möglichkeit, integrierte Sprachoperatoren auf verschiedene Typen anzuwenden, einschließlich Benutzertypen. In der Tat ist dies eine ziemlich alte Idee. Bereits in den ersten Programmiersprachen, Symbole arithmetischer Operationen : +.-, usw. wurden für Operationen mit ganzen Zahlen und reellen Zahlen verwendet, obwohl sie unterschiedliche Größen und unterschiedliche interne Darstellungen haben, und dementsprechend werden diese Operationen auf unterschiedliche Weise implementiert. Mit dem Aufkommen objektorientierter Sprachen wurde diese Idee weiterentwickelt. Wenn Operationen für benutzerdefinierte Typen eine ähnliche Semantik haben wie Operationen für integrierte Typen, können Sie die Syntax der integrierten Operatoren verwenden. Dies kann die Lesbarkeit des Codes verbessern, ihn prägnanter und aussagekräftiger machen und das Schreiben von verallgemeinertem Code vereinfachen. In C ++ wird das Überladen von Operatoren ernsthaft unterstützt und in der Standardbibliothek aktiv verwendet.



1. Allgemeine Probleme der BedienerĂĽberlastung



1.1. Ăśberladene Operatoren


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.


, , . , ( ), .


Wenn das erforderliche Funktionsobjekt nicht angegeben ist, wird der Operator <standardmäßig in einem Algorithmus verwendet std::lexicographical_compare(), der Bereiche, in minimalen / maximalen Suchalgorithmen ( min_element()usw.), in Algorithmen zum Sortieren und sortierten Daten ( std::sort()usw.) in pyramidenbezogenen Algorithmen vergleicht ( std::make_heap()usw.).

Der Operator wird ==standardmäßig in einem Algorithmus verwendet std::equal(), der Bereiche vergleicht, in einem Algorithmus std::count(), der die Anzahl der angegebenen Elemente zählt, in Suchalgorithmen ( std::find()usw.), in Algorithmen std::replace()und zum std::remove()Ändern eines Bereichs.


Der Operator wird +standardmäßig im Algorithmus verwendet accumulate(). (Einzelheiten siehe Anhang 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 ++ - Standardbibliothek: Referenzhandbuch, 2. Ausgabe: Per. aus dem Englischen - M.: LLC “I.D. Williams ", 2014.
[Dewhurst]
Dewhurst, Stefan K. Slippery platziert C ++. So vermeiden Sie Probleme beim Entwerfen und Kompilieren Ihrer Programme .: Per. aus dem Englischen - M.: DMK Press, 2012.
[Meyers1]
Meyers, Scott. Effektive Nutzung von C ++. 55 sichere Möglichkeiten zur Verbesserung der Struktur und des Codes Ihrer Programme .: Per. aus dem Englischen - M.: DMK Press, 2014.
[Sutter1]
Sutter, Wappen. Die Lösung komplexer Probleme in C ++ .: Per. aus dem Englischen - M: LLC “I.D. Williams, 2015.


All Articles