Surcharge en C ++. Partie I. Fonctions et modèles de surcharge


Le C ++ est un langage complexe et intéressant, vous pouvez vous y perfectionner presque toute votre vie. À un moment donné, j'ai voulu l'étudier comme suit: prendre un aspect de la langue, peut-être assez étroit, et le traiter aussi profondément et en détail que possible. Cette approche a été largement stimulée par les livres remarquables de Scott Meyers, Herb Sutter et Stephen Dewhurst. Quand une certaine quantité de matériel s'est accumulée, j'ai décidé de leur présenter Khabrovchan. Il y avait donc cette série, que j'ai appelé "C ++, creuser en profondeur." La série est marquée comme tutoriel, mais elle est toujours axée non pas sur les débutants, mais plutôt sur le niveau intermédiaire. Le premier sujet est la surcharge en C ++. Le sujet s'est avéré très étendu et nous avons reçu trois articles. Le premier article concerne la surcharge des fonctions et des modèles, le second la surcharge des opérateurs et le troisième la surcharge des opérateursnew/delete. Commençons donc à creuser.



Table des matières


Table des matières

  
  1.
    1.1.
    1.2.
    1.3.
      1.3.1.
      1.3.2.
      1.3.3.
    1.4.
      1.4.1. using-
      1.4.2. using-
      1.4.3. using-
      1.4.4. ,
  2.
    2.1. «»
    2.2.
    2.3.
    2.4.
    2.5.
      2.5.1.
      2.5.2. SFINAE
      2.5.3.
      2.5.4.
    2.6. «»
      2.6.1. ,
      2.6.2. Rvalue
      2.6.3.
  3. ,
    3.1.
    3.2.
    3.3.
    3.4.
    3.5.
  4.
  
     . Visitor
     .
  




introduction


Au sens large, la surcharge est la possibilité d'utiliser simultanément plusieurs fonctions portant le même nom. Le compilateur les distingue du fait qu'ils ont un ensemble de paramètres différent. Aux points d'appel, le compilateur analyse les types d'arguments et détermine quelle fonction particulière doit être appelée. Dans la littérature de langue russe, le terme «partage» peut parfois être trouvé, mais il ne semble pas avoir pris racine.


La surcharge est prise en charge par de nombreux langages de programmation, nous ne considérerons que C ++ 17.



1. Dispositions générales



1.1. Fonctions surchargées


( ) (overloaded), (scope) . , (=delete) .


void Foo();
char Foo(); // 
void Foo(int x);
void Foo(int x) noexcept;  // 
void Foo(double x);
void Foo(double) = delete; // 

, .


, . (decay) ,


void Foo(int x[4]);
void Foo(int x[]);
void Foo(int *x);

, .


.


, , const ( volatile),


void Foo(int x);
void Foo(const int x);

, .



1.2.


. (lookup) , (candidate functions). (template argument deduction). ( ). , . , . , , , « » (match the arguments most closely). (overload resolution). , , (ambiguous call to overloaded function). :


void Foo(float x);
void Foo(double x);

Foo("meow") , Foo(42) , , Foo(3.14f) Foo(3.14) .


(overload resolution rules) ( , ), , . .


« » : - . .


— . ( private protected). ( =delete). , . , , .



1.3.


, . . , . . , , , . () .


, . . , . , , . (hide) . , , , , , .



1.3.1.


, , . .


class X {/* ... */};
// ...
X x;
x.Foo();
X::Foo(42);

X. X.


namespace N {/* ... */}
// ...
N::Foo();

N, .


::Foo();

, .


«» .


, , .


( ), , .




{
    Foo();
}

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



1.3.2.


.


class B
{
// ...
public:
    void Foo(int x);
};

class D : public B
{
// ...
public:
    D();
    void Foo(double x);
};
// ...
D d;
d.Foo(42);

Foo, ? D::Foo(double), B::Foo(int) . ( D), , ( B) . D::Foo(double) . D::Foo(double) , , B::Foo(int) , . , D Foo, B B::Foo(int).


. C++ , , . . , . ( , .)



1.3.3.


C++, . ( ), :


{
    void Foo();
    void Foo(int x);
// ...
    Foo(42);
// ...
}

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



1.4.


using- using-. , , .


, , , , , .



1.4.1. using-


:


class B
{
// ...
public:
    void Foo(int x);
};
class D : public B
{
// ...
public:
    using B::Foo;
    void Foo(double x);
};
// ...
D d;
d.Foo(42);

Foo B, B::Foo(int).



1.4.2. using-


using- , . using- . , , using-, , . :


namespace N
{
    void Foo(int x);
}

void Foo(const char* x);
// ...
void Test()
{
    using N::Foo; //  Foo(const char*)
    Foo(42);      // OK, N::Foo(int x)
    Foo("meow");  // , Foo(const char*) 
}

, Foo , using- :


namespace N
{
    void Foo(int x);
}

void Foo(const char* x);
// ...
using N::Foo;
void Test()
{
    Foo(42);      // OK, N::Foo(int)
    Foo("meow");  // OK, Foo(const char*)
}


1.4.3. using-


N.


using namespace N;

using-. N N::. N, using- (, using-, ). , , using- , .


, , , , . ( using-.)


namespace
{
    void Foo(int x);
}

void Foo(const char* x);
// ...
Foo(42);     // OK, Foo(int),   
Foo("meow"); // OK, Foo(const char*)


1.4.4. ,


, . :


namespace N
{
    class X {/* ... */};
    void Foo(const X& x);
}

( N):


N::X x;
Foo(x);

N , , N::Foo(const X&). , (argument depended lookup, ADL), . ADL , .



2.


, .



2.1. «»


C++ . , . , , . : , , .


bool. bool int bool. . bool int.


void Foo(int x);
void Foo(bool x);
// ...
int x = 6, y = 5;
Foo(x == y); // Foo(bool)
Foo(x = y);  // Foo(int)



void Foo(bool x, int y);
void Foo(int x, bool y);

, :


Foo(1, 2);

, . int.


enum Qq { One = 1, Two };
void Foo(int x);
void Foo(Qq x);
// ...
Foo(One); // Foo(Qq)
Foo(42);  // Foo(int)

, , int long .


void Foo(int x);
void Foo(long x);
// ...
Foo(42);  // Foo(int)
Foo(42L); // Foo(long)

. . , , . :


void Foo(long x);
void Foo(long long x);

void Foo(float) = delete;
void Foo(double) = delete;
void Foo(long double) = delete;


2.2.


C++11 — std::nullptr_t nullptr. .


void Foo(int x);
void Foo(void* x);
// ...
Foo(0);       // Foo(int)
Foo(nullptr); // Foo(void*)

C++98


Foo((void*)0);

. nullptr , nullptr.


void Foo(void* x);
void Foo(std::nullptr_t);
// ...
void* x = nullptr;
Foo(x);       // Foo(void*)
Foo(nullptr); // Foo(std::nullptr_t)

.



2.3.


++11 (uniform initialization) — std::intializer_list<>. , , . . .


  1. {}, . , .
  2. , std::intializer_list<>. std::intializer_list<>, , . , std::intializer_list<>, ( ), .

std::vector<T>, std::intializer_list<T>.


:


std::vector<int> v1(3, 1), v2{3, 1};

v1 — 3 1. v2 — 2 3 1, std::intializer_list<int>, , .


:

std::vector<const char*> u1(3, "meow"), u2{3, "meow"};

u1 u2 , 3. u2 std::intializer_list<const char*> , u1.


:


std::vector<bool> b1(3, true), b2{3, true};

b1 3 , true. b2 , std::intializer_list<bool> int bool.


[Meyers2].



2.4.


, ... , , . .



2.5.


, , . — c .


. ( , ), .



2.5.1.


. , . , . , .


, , . , . , , . [Sutter2].


C++11 (variadic templates). , , , .



2.5.2. SFINAE


, , . :


template<typename T>
void Foo(const T* x);
// ...
Foo(42);

, , , , , «» . SFINAE, Substitution Failure is not an Error ( ).



2.5.3.


, .


void Foo(int x); //  
template<typename T>
void Foo(T x); //  1
template<>
void Foo<double>(double x); //    1  double
template<>
void Foo<const char*>(const char* x); //    1  const char*
template<typename T>
void Foo(const T* x); //  2,     1
template<typename T>
class U {/* ... */};
template<typename T>
void Foo(U<T> u); //  3,     1

, :


Foo(42);       // #1 —  
Foo(3.14);     // #2 —    1  double
Foo(42L);      // #3 —   1  long
Foo("meow");   // #4 —   2  char
Foo(U<int>()); // #5 —   3  int

: 1 int. .


, double int. : 1 double 1 double, .


, long int. : 1 long, .


, : 1 const char* 2 char. 2 , , 1 const char* .


, : 1 U<int> 3 int. 3 .



2.5.4.


:


void Foo(int x);     //  
template<typename T> // 
void Foo(T x);

int, int&, const int, const int&, (long, short, unsigned int, etc.) . , (greedy). , , , . . , . — (template disabling). :


template<
    typename T,
    typename S = std::enable_if_t<!std::is_integral<T>::value>>
void Foo(T x);

SFINAE , , .


, , , :


template<typename T> //  
void FooInt(T x);
template<typename T> //  
void FooEx(T x);
// C++17
template<typename T>
void Foo(T x)
{
    if constexpr (std::is_integral<T>::value)
    {
        FooInt(x);
    }
    else
    {
        FooEx(x);
    }
}
// C++11
template<typename T>
void Foo(T x)
{
    std::is_integral<T>::value
        ? FooInt(x)
        : FooEx(x);
}

<type_traits>.



2.6. «»


«» : , , , rvalue .


. :


  1. lvalue — ;
  2. () lvalue — ;
  3. rvalue — lvalue, std::move();
  4. () rvalue — .

— .


, .
:


void Foo(T& x);

lvalue.


rvalue-:


void Foo(T&& x);

rvalue.


:


void Foo(const T& x);
void Foo(T x);

.



2.6.1. ,


:


void Foo(T& x);
void Foo(const T& x);

lvalue ( ), .


:


void Foo(T& x);
void Foo(T x);

rvalue , lvalue .


:


void Foo(const T& x);
void Foo(T x);

.


- const this.


class X
{
public:
    X();
    void Foo();       // this   X*
    void Foo() const; // this   const X*
    void DoSomething() const;
    void DoSomethingElse();
// ...
};
void X::DoSomething() const
{
// ...
    Foo(); // Foo() const
// ...
}
void X::DoSomethingElse()
{
// ...
    Foo(); // Foo()
// ...
}
// ...
X x;
x.Foo();  // Foo()
const X cx;
cx.Foo(); // Foo() const

H - rvalue , rvalue. rvalue .


class X
{
public:
    X();
    void Swap(X& other) noexcept;
// ...
};
// ...
X x;
// ...
X().Swap(x); // OK
x.Swap(X()); // ,      

rvalue . , . rvalue . ( , rvalue .) rvalue . , , , , . [Sutter1].



2.6.2. Rvalue


C++11 . — rvalue-. Rvalue- C++ , , rvalue-. , , «» .


:


void Foo(T&& x);
void Foo(const T& x);

rvalue ( ), .


:


void Foo(T&& x);
void Foo(T x);

lvalue , rvalue , .


:


void Foo(T&& x);
void Foo(T& x);

rvalue , lvalue , .


, — rvalue, — , , . ( .) , , , .


++11, rvalue- — -. (lvalue/rvalue) this.


class X
{
public:
    X();
    void Foo() &;  // this   lvalue
    void Foo() &&; // this   rvalue
// ...
}; 
// ...
X x;
x.Foo ();  // Foo() &
X().Foo(); // Foo() &&

: rvalue- lvalue. , rvalue-, lvalue , - , std::move(), rvalue- .



2.6.3.




template<typename T>
void Foo(T&& x);

rvalue-, (universal reference). . lvalue x T&, const T&, rvalue T&&. x lvalue, - std::forward<T>(), , T&&, rvalue. (greedy), . , 2.5. (perfect forwarding) . [Meyers2].



3. ,



3.1.


C++ , - (, , , ), . (incomplete declaration), (forward declaration). . .


class X; //   X
class Y; //   Y
void Foo(X* x);
void Foo(Y* y);
// ...
X* px;
// ...
Foo(px); // void Foo(X* x);

X , .



3.2.


.


void Foo(int x);
void Foo(const char* x);
// ...
void (*pF)(int) = Foo; // Foo(int)

, . (, ) , ADL .


namespace N
{
    class X {/* ... */};
    void Foo(const X& x);
}
// ...
void (*pF)(const N::X&) = Foo;    // 
void (*pF)(const N::X&) = N::Foo; // OK
using N::Foo;
void (*pF)(const N::X&) = Foo;    // OK

.


void Foo(int x);
void Foo(const char* x);
// ...
auto pf = static_cast<void(*)(int)>(Foo); // Foo(int)

ADL .


, auto.


auto pf = Foo;

Foo , , pf Foo. Foo, , .


, (). std::function<>, , , std::sort(). .


- - .


class X
{
// ...
    void Foo(int a);
    void Foo(const char* a);
};
// ...
void (X::*pF)(int) = &X::Foo; // X::Foo(int)


3.3.


.



void Foo(int x);
void Foo() { Foo(0); }



void Foo(int x = 0);

, Foo(), . , .


void (*pF1)(int) = Foo; // OK
void (*pF0)() = Foo;    // , Foo    

.


, , .



3.4.


. , , , , , . («» , . 1.3). [Dewhurst]. — . , ( , - ), , . . , Visitor.



3.5.


, , — , . , . , .


, . -, std::input_iterator_tag. (typedef) — iterator_category. , -, iterator_category, , . :


void DisplayIterCat(std::input_iterator_tag tag)
{
    std::cout << "Input iterator\n";
}
//  DisplayIterCat   -
template<class Iter>
void DisplayIterCat(Iter it)
{
    DisplayIterCat(typename Iter::iterator_category());
}

typename. , iterator_category Iter.


C++ , , . :


template<typename T, T val>
struct integral_constant;

T T, , . :


typedef integral_constant<bool, true> true_type;
typedef integral_constant<bool, false> false_type;

, , .


, . :


sizeof(Foo(expr))

. expr , . , Foo , Foo . .


C++17 if constexpr (), , . ( ) .



4.


— , . , .


, . , . , .


, .


— .





. Visitor


A, V


void DoDblDispatchOper(A* a, V* v);

a v , , a, v, . . , , . Visitor — [GoF].


, IAcceptor IVisitor.


class IVisitor;
class IAcceptor
{
public:
    virtual void Accept(IVisitor* visitor) = 0;
// ...
};
class A1;
class A2;
// ...
class IVisitor
{
public:
    virtual void Visit(A1* a) = 0;
    virtual void Visit(A2* a) = 0;
// ...
};
class A1 : public IAcceptor 
{
    void Accept(IVisitor* visitor) override
    {
        visitor->Visit(this);
    }
// ...
};
class A2 : public IAcceptor
{
    void Accept(IVisitor* visitor) override
    {
        visitor->Visit(this);
    }
// ...
};
// ...
class V1 : public IVisitor {/* ... */};
class V2 : public IVisitor {/* ... */};
// ...

IVisitor Visit() , IAcceptor. Visit() , .




void IAcceptor::Accept(IVisitor* visitor);

, :


visitor->Visit(this);

this , Visit(). Visit(), visitor.


void DoDblDispatchOper(IAcceptor* acceptor, IVisitor* visitor)
{
    acceptor->Accept(visitor);
}

Voila. .



.


- . — ( ) .


template<typename T>
void swap(T& a, T& b);

, , . . , std::swap() , , . , std::swap(), , , . , .


1. - Swap() ( ), .


class X
{
public:
    void Swap(X& other) noexcept;
// ...
};

, , C++11 noexcept.


2. , X ( , ), (-) swap() ( ):


inline void swap(X& a, X& b) noexcept { a.Swap(b); }

, ADL, std::swap() .


3. std::swap() X


namespace std
{
    template<>
    void swap<X>(X& a, X& b) noexcept { a.Swap(b); }
};

std , - . std.


, ? — . , , .


namespace N
{
    template<typename T>
// ...
    T x, y;
// ...
    swap(x, y);
// ...

, T swap(), , . std::swap() , , .



:


std::swap(x, y);

swap() std::swap() — . .


:


using std::swap;
swap(x, y);

, . std::swap(), . std::swap(). std::swap() .


, ( std)


std::swap(x, y);



swap(x, y);

[Meyers1] , .


, .


template<typename T>
class X
{
// ...
public:
    void Swap(X& other) noexcept;
};
template<typename T>
void swap(X<T>& a, X<T>& b) noexcept { a.Swap(b); }

std::swap() , std , , , , .


friend swap() :


template<typename T>
class X
{
// ...
    void Swap(X& other) noexcept;
    friend void swap(X& a, X& b) noexcept { a.Swap(b); }
};

, - Swap() .




[GoF]
., ., ., . - . .: . . — .: , 2001.
[Dewhurst]
, . C++. .: . . — .: , 2012.
[Meyers1]
, . C++. 55 .: . . — .: , 2014.
[Meyers2]
, . C++: 42 C++11 C ++14.: . . — .: «.. », 2016.
[Sutter1]
, . C++.: . . — : «.. », 2015.
[Sutter2]
Sutter, armoiries. Nouvelles tâches complexes en C ++.: Per. de l'anglais - M: LLC «I.D. Williams, 2015.



Source: https://habr.com/ru/post/undefined/


All Articles