Nous continuons la sĂ©rie "C ++, creuser en profondeur." Le but de cette sĂ©rie est de vous en dire le plus possible sur les diffĂ©rentes fonctionnalitĂ©s du langage, peut-ĂȘtre assez particuliĂšres. Cet article concerne la surcharge des opĂ©rateurs. Une attention particuliĂšre est portĂ©e Ă l'utilisation d'opĂ©rateurs surchargĂ©s dans la bibliothĂšque standard. Ceci est le deuxiĂšme article de la sĂ©rie, le premier dĂ©diĂ© Ă la surcharge des fonctions et des modĂšles, est ici . Le prochain article se concentrera sur la surcharge des instructions de gestion de la mĂ©moire.
Table des matiĂšres
introduction
La surcharge d'opĂ©rateur est la possibilitĂ© d'appliquer des opĂ©rateurs de langage intĂ©grĂ©s Ă diffĂ©rents types, y compris les types d'utilisateurs. En fait, c'est une idĂ©e assez ancienne. DĂ©jĂ dans les premiĂšres langues de programmation, symboles d'opĂ©rations arithmĂ©tiques: +.-, etc. ont Ă©tĂ© utilisĂ©es pour des opĂ©rations sur des nombres entiers et rĂ©els, malgrĂ© le fait qu'elles ont des tailles et des reprĂ©sentations internes diffĂ©rentes et, par consĂ©quent, ces opĂ©rations sont mises en Ćuvre de diffĂ©rentes maniĂšres. Avec l'avĂšnement des langages orientĂ©s objet, cette idĂ©e a Ă©tĂ© dĂ©veloppĂ©e davantage. Si les opĂ©rations sur les types dĂ©finis par l'utilisateur ont une sĂ©mantique similaire aux opĂ©rations sur les types intĂ©grĂ©s, alors pourquoi ne pas utiliser la syntaxe des opĂ©rateurs intĂ©grĂ©s. Cela peut augmenter la lisibilitĂ© du code, le rendre plus concis et expressif, simplifier l'Ă©criture du code gĂ©nĂ©ralisĂ©. En C ++, la surcharge d'opĂ©rateur a un support sĂ©rieux et est activement utilisĂ©e dans la bibliothĂšque standard.
1. ProblÚmes généraux de surcharge de l'opérateur
1.1. Opérateurs surchargés
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';
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, - .
template<typename T>
class Rational
{
T num;
T den;
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<int> r4 = r1 + 3;
Rational<int> r5 = 4 + r2;
Rational int. int int Rational, .
-.
Rational<int> r6 = r1 - 3;
Rational<int> r7 = 4 - r2;
, 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<>, . , . , , -. .
- .
- .
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<>.
, .
- , . , , , , ( ).
- , .
- .
C- .
3.4.5.
, . , (deleter). () . std::unique_ptr<> std::default_delete<>, delete ( delete[] ). std::shared_ptr<> delete. , . .
std::unique_ptr<> -.- .
- std::shared_ptr<>, std::shared_ptr<>.
3.4.6.
, , . , ( ), .
Si l'objet fonctionnel nécessaire n'est pas spécifié, l'opérateur est <utilisé par défaut dans un algorithme std::lexicographical_compare()qui compare les plages, dans les algorithmes de recherche minimum / maximum ( min_element(), etc.), dans les algorithmes liés au tri et aux données triées ( std::sort(), etc.), dans les algorithmes liés aux pyramides ( std::make_heap(), etc.).L'opérateur est ==utilisé par défaut dans un algorithme std::equal()qui compare les plages, dans un algorithme std::count()qui compte le nombre d'éléments spécifiés, dans les algorithmes de recherche ( std::find(), etc.), dans les algorithmes std::replace()et std::remove()qui modifient une plage.
L'opérateur est +utilisé par défaut dans l'algorithme accumulate(). (Pour plus de détails, voir l'annexe A.)
, .
- , . , .
- , . ( -.)
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);
std::visit(v, b);
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;
}
};
}
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. de l'anglais - M.: LLC «I.D. Williams ", 2014.
[Dewhurst]
Dewhurst, Stefan K. Slippery place C ++. Comment éviter les problÚmes lors de la conception et de la compilation de vos programmes.: Per. de l'anglais - M .: DMK Press, 2012.
[Meyers1]
Meyers, Scott. Utilisation efficace de C ++. 55 façons sûres d'améliorer la structure et le code de vos programmes.: Per. de l'anglais - M .: DMK Press, 2014.
[Sutter1]
Sutter, Armoiries. La solution de problÚmes complexes en C ++.: Per. de l'anglais - M: LLC «I.D. Williams, 2015.