Überladen in C ++. Teil I. Überladen von Funktionen und Vorlagen


C ++ ist eine komplexe und interessante Sprache, die Sie fast Ihr ganzes Leben lang verbessern können. Irgendwann wollte ich es wie folgt studieren: Nehmen Sie einen Aspekt der Sprache, möglicherweise ziemlich eng, und beschĂ€ftigen Sie sich so tief und detailliert wie möglich damit. Dieser Ansatz wurde weitgehend durch die bemerkenswerten BĂŒcher von Scott Meyers, Herb Sutter und Stephen Dewhurst angeregt. Als sich eine bestimmte Menge an Materialien ansammelte, beschloss ich, Khabrovchan vorzustellen. Es gab also diese Serie, die ich "C ++, Graben in der Tiefe" nannte. Die Serie ist als Tutorial gekennzeichnet, konzentriert sich jedoch nicht auf AnfĂ€nger, sondern auf die Mittelstufe. Das erste Thema ist das Überladen in C ++. Das Thema war sehr umfangreich und wir haben drei Artikel erhalten. Der erste Artikel befasst sich mit dem Überladen von Funktionen und Vorlagen, der zweite mit dem Überladen von Operatoren und der dritte mit dem Überladen von Operatorennew/delete. Also fangen wir an zu graben.



Inhaltsverzeichnis


Inhaltsverzeichnis

  
  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
     .
  




EinfĂŒhrung


Überlastung ist im weitesten Sinne die FĂ€higkeit, mehrere Funktionen mit demselben Namen gleichzeitig zu verwenden. Der Compiler unterscheidet sie dadurch, dass sie unterschiedliche Parameter haben. An Aufrufpunkten analysiert der Compiler die Arten von Argumenten und bestimmt, welche bestimmte Funktion aufgerufen werden soll. In der russischsprachigen Literatur findet sich manchmal der Begriff „Teilen“, der jedoch offenbar keine Wurzeln geschlagen hat.


Das Überladen wird von vielen Programmiersprachen unterstĂŒtzt, wir werden nur C ++ 17 betrachten.



1. Allgemeine Bestimmungen



1.1. Überladene Funktionen


( ) (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, Wappen. Neue komplexe Aufgaben in C ++ .: Per. aus dem Englischen - M: LLC “I.D. Williams, 2015.



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


All Articles