Sobrecarga en C ++. Parte II Sobrecarga del operador



Continuamos la serie "C ++, cavando en profundidad". El propósito de esta serie es informarle lo más posible sobre las diversas características del lenguaje, posiblemente bastante especial. Este artículo trata sobre la sobrecarga del operador. Se presta especial atención al uso de operadores sobrecargados en la biblioteca estándar. Este es el segundo artículo de la serie, el primero dedicado a sobrecargar funciones y plantillas, está aquí . El siguiente artículo se centrará en sobrecargar las declaraciones de administración de memoria.



Tabla de contenido


Tabla de contenido

  
  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-
  




Introducción


La sobrecarga de operadores es la capacidad de aplicar operadores de lenguaje integrados a diferentes tipos, incluidos los tipos de usuario. De hecho, esta es una idea bastante antigua. Ya en los primeros lenguajes de programación, símbolos de operaciones aritméticas: +.-etc. se utilizaron para operaciones en números enteros y reales, a pesar de que tienen diferentes tamaños y diferentes representaciones internas y, en consecuencia, estas operaciones se implementan de diferentes maneras. Con la llegada de los lenguajes orientados a objetos, esta idea se desarrolló aún más. Si las operaciones en tipos definidos por el usuario tienen una semántica similar a las operaciones en tipos integrados, entonces, ¿por qué no utilizar la sintaxis de los operadores integrados? Esto puede aumentar la legibilidad del código, hacerlo más conciso y expresivo, simplificar la escritura de código generalizado. En C ++, la sobrecarga del operador tiene un soporte serio y se usa activamente en la biblioteca estándar.



1. Problemas generales de sobrecarga del operador



1.1. Operadores sobrecargados


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.


, , . , ( ), .


Si no se especifica el objeto funcional necesario, el operador se <usa por defecto en un algoritmo std::lexicographical_compare()que compara rangos, en algoritmos de búsqueda mínimo / máximo ( min_element(), etc.), en algoritmos relacionados con la ordenación y datos ordenados ( std::sort(), etc.), en algoritmos relacionados con pirámides ( std::make_heap(), etc.)

El operador se ==usa por defecto en un algoritmo std::equal()que compara rangos, en un algoritmo std::count()que cuenta el número de elementos especificados, en algoritmos de búsqueda ( std::find(), etc.), en algoritmos std::replace()y std::remove()que modifican un rango.


El operador se +usa por defecto en el algoritmo accumulate(). (Para más detalles, consulte el Apéndice 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. Biblioteca estándar de C ++: Guía de referencia, 2ª ed .: Per. De inglés - M .: LLC "I.D. Williams ", 2014.
[Dewhurst]
Dewhurst, Stefan K. Slippery coloca C ++. Cómo evitar problemas al diseñar y compilar sus programas.: Per. De inglés - M .: DMK Press, 2012.
[Meyers1]
Meyers, Scott. Uso efectivo de C ++. 55 formas seguras de mejorar la estructura y el código de sus programas.: Per. De inglés - M .: DMK Press, 2014.
[Sutter1]
Sutter, Escudo de Armas. La solución de problemas complejos en C ++.: Per. De inglés - M: LLC "I.D. Williams, 2015.


All Articles