16 Overloading [over]

Когда два или более разных объявления указаны для одного имени в одной и той же области, это имя называется overloaded. По расширению вызываются два объявления в одной и той же области, которые объявляют одно и то же имя, но с разными типами overloaded declarations. Могут быть перегружены только объявления функций и шаблонов функций; объявления переменных и типов не могут быть перегружены.

Когда в вызове используется имя перегруженной функции, ссылка на объявление перегруженной функции определяется путем сравнения типов аргументов в точке использования с типами параметров в перегруженных объявлениях, которые видны в точке использования. . Этот процесс выбора функции вызывается overload resolution и определяется в [over.match]. [Example:

double abs(double);
int abs(int);

abs(1);             // calls abs(int);
abs(1.0);           // calls abs(double);

end example]

16.1 Overloadable declarations [over.load]

Не все объявления функций могут быть перегружены. Здесь указаны те, которые нельзя перегрузить. Программа плохо сформирована, если она содержит два таких неперегружаемых объявления в одной и той же области видимости. [ Note: Это ограничение применяется к явным объявлениям в области видимости, а также между такими объявлениями и объявлениями, сделанными через using-declaration. Это не относится к наборам функций, созданным в результате поиска имени (например, из-за using-directives) или разрешения перегрузки (например, для операторских функций). ]end note

Некоторые объявления функций не могут быть перегружены:

  • Объявления функций, которые различаются только типом возвращаемого значения, exception specificationили и тем , и другим, не могут быть перегружены.

  • Объявления функций-членов с тем же именем и одинаковыми именами parameter-type-list не могут быть перегружены, если любое из них является static объявлением функции-члена ([class.static]). Точно так же объявления шаблонов функций-членов с тем же именем, одним и тем же списком типов-параметров и одними и теми же списками параметров шаблона не могут быть перегружены, если какое-либо из них является static объявлением шаблона функции-члена. Типы неявных параметров объекта, созданных для функций-членов с целью разрешения перегрузки ([over.match.funcs]), не учитываются при сравнении списков типов параметров для применения этого правила. Напротив, если нет static объявления функции-члена среди набора объявлений функций-членов с тем же именем и одним и тем же списком типов-параметров, то эти объявления функций-членов могут быть перегружены, если они различаются по типу параметра неявного объекта. [ Example: Следующее иллюстрирует это различие:

    class X {
      static void f();
      void f();                     // ill-formed
      void f() const;               // ill-formed
      void f() const volatile;      // ill-formed
      void g();
      void g() const;               // OK: no static g
      void g() const volatile;      // OK: no static g
    };

    end example]

  • Объявления функций-членов с тем же именем иparameter-type-list такими же, а также объявления шаблонов функций-членов с тем же именем, одним и тем же списком типов-параметров и одними и теми же списками параметров шаблона не могут быть перегружены, если любой из них, но не все, имеет ref-qualifier([dcl.fct]). [Example:

    class Y {
      void h() &;
      void h() const &;             // OK
      void h() &&;                  // OK, all declarations have a ref-qualifier
      void i() &;
      void i() const;               // ill-formed, prior declaration of i
                                    // has a ref-qualifier
    };

    end example]

[ Note: Как указано в [dcl.fct], объявления функций с эквивалентными объявлениями параметров объявляют одну и ту же функцию и поэтому не могут быть перегружены:

  • Объявления параметров, которые отличаются только использованием эквивалентных typedef «типов», эквивалентны. A typedef - это не отдельный тип, а только синоним другого типа ([dcl.typedef]). [Example:

    typedef int Int;
    
    void f(int i);
    void f(Int i);                  // OK: redeclaration of f(int)
    void f(int i) { /* ... */ }
    void f(Int i) { /* ... */ }     // error: redefinition of f(int)
    

    end example]

    С другой стороны, перечисления относятся к разным типам и могут использоваться для различения объявлений перегруженных функций. [Example:

    enum E { a };
    
    void f(int i) { /* ... */ }
    void f(E i)   { /* ... */ }

    end example]

  • Объявления параметров, которые отличаются только указателем * от массива [] , эквивалентны. То есть объявление массива корректируется, чтобы стать объявлением указателя ([dcl.fct]). Только второе и последующие измерения массива имеют значение в параметрах types ([dcl.array]). [Example:

    int f(char*);
    int f(char[]);                  // same as f(char*);
    int f(char[7]);                 // same as f(char*);
    int f(char[9]);                 // same as f(char*);
    
    int g(char(*)[10]);
    int g(char[5][10]);             // same as g(char(*)[10]);
    int g(char[7][10]);             // same as g(char(*)[10]);
    int g(char(*)[20]);             // different from g(char(*)[10]);
    

    end example]

  • Объявления параметров, которые отличаются только тем, что один является типом функции, а другой - указателем на тот же тип функции, эквивалентны. То есть тип функции корректируется, чтобы стать указателем на функцию type ([dcl.fct]). [Example:

    void h(int());
    void h(int (*)());              // redeclaration of h(int())
    void h(int x()) { }             // definition of h(int())
    void h(int (*x)()) { }          // ill-formed: redefinition of h(int())
    

    end example]

  • Объявления параметров, которые отличаются только наличием или отсутствием const и / или volatile эквивалентны. То есть, const и volatile типа-спецификаторы для каждого типа параметра игнорируется при определении того, какие функции объявляются, определяются или вызываются. [Example:

    typedef const int cInt;
    
    int f (int);
    int f (const int);              // redeclaration of f(int)
    int f (int) { /* ... */ }       // definition of f(int)
    int f (cInt) { /* ... */ }      // error: redefinition of f(int)
    

    end example]

    Только const и volatile на самом внешнем уровне спецификации типа параметра игнорируется таким образом спецификаторы типа; const а volatile спецификаторы типа, скрытые в спецификации типа параметра, имеют важное значение и могут использоваться для различения объявлений перегруженных функций.123 В частности, для любого типа T«указатель на T», «указатель на const T» и «указатель на volatile T» считаются отдельными типами параметров, как и «ссылка на T», «ссылка на const T» и «ссылка на volatile T».

  • Два объявления параметров, которые различаются только аргументами по умолчанию, эквивалентны. [ Example: Учтите следующее:

    void f (int i, int j);
    void f (int i, int j = 99);     // OK: redeclaration of f(int, int)
    void f (int i = 88, int j);     // OK: redeclaration of f(int, int)
    void f ();                      // OK: overloaded declaration of f
    
    void prog () {
        f (1, 2);                   // OK: call f(int, int)
        f (1);                      // OK: call f(int, int)
        f ();                       // Error: f(int, int) or f()?
    }

    end example]

end note]

Когда тип параметра включает в себя тип функции, например, в случае типа параметра , который представляет собой указатель на функцию, то const и volatile типа спецификаторы на внешнем уровне спецификаций типа параметра для внутреннего типа функции также игнорируются.

16.2 Declaration matching [over.dcl]

Два объявления функции с одним и тем же именем относятся к одной и той же функции, если они находятся в одной области и имеют эквивалентные объявления параметров ([over.load]). Функциональный член производного класса находится not в той же области видимости, что и функциональный член с тем же именем в базовом классе. [Example:

struct B {
  int f(int);
};

struct D : B {
  int f(const char*);
};

Здесь D​::​f(const char*) скрывается, B​::​f(int) а не перегружается.

void h(D* pd) {
  pd->f(1);                     // error:
                                // D​::​f(const char*) hides B​::​f(int)
  pd->B::f(1);                  // OK
  pd->f("Ben");                 // OK, calls D​::​f
}

end example]

Локально объявленная функция не находится в той же области видимости, что и функция в содержащей ее области. [Example:

void f(const char*);
void g() {
  extern void f(int);
  f("asdf");                    // error: f(int) hides f(const char*)
                                // so there is no f(const char*) in this scope
}

void caller () {
  extern void callee(int, int);
  {
    extern void callee(int);    // hides callee(int, int)
    callee(88, 99);             // error: only callee(int) in scope
  }
}

end example]

Разным версиям перегруженной функции-члена могут быть предоставлены разные правила доступа. [Example:

class buffer {
private:
    char* p;
    int size;
protected:
    buffer(int s, char* store) { size = s; p = store; }
public:
    buffer(int s) { p = new char[size = s]; }
};

end example]

16.3 Overload resolution [over.match]

Разрешение перегрузки - это механизм выбора наилучшей функции для вызова с учетом списка выражений, которые должны быть аргументами вызова, и их набор candidate functions может быть вызван в зависимости от контекста вызова. Критериями выбора лучшей функции являются количество аргументов, насколько хорошо аргументы соответствуют списку типов-параметров функции-кандидата, насколько хорошо (для нестатических функций-членов) объект соответствует неявному параметру объекта и некоторые другие свойства функции кандидата. [ Note: Не гарантируется, что функция, выбранная с помощью разрешения перегрузки, подходит для контекста. Другие ограничения, такие как доступность функции, могут сделать ее использование в вызывающем контексте некорректным. ]end note

Разрешение перегрузки выбирает функцию для вызова в семи различных контекстах в пределах языка:

  • вызов функции, названной в function call syntax;

  • вызов оператора вызова функции, функции преобразования указателя в функцию, функции преобразования ссылки в указатель на функцию или функции преобразования ссылки в функцию для объекта класса, указанного в синтаксисе вызова функции ([over.call.object]) ;

  • вызов оператора, указанного в выражении ([over.match.oper]);

  • вызов конструктора для инициализации по умолчанию или прямой инициализации ([dcl.init]) объекта класса ([over.match.ctor]);

  • вызов пользовательского преобразования для copy-initialization объекта класса ([over.match.copy]);

  • вызов функции преобразования для инициализации объекта неклассового типа из выражения типа класса ([over.match.conv]); а также

  • вызов функции преобразования для преобразования в glvalue или класс prvalue, на который будет ссылаться ([dcl.init.ref]) directly bound.

Каждый из этих контекстов по-своему определяет набор функций-кандидатов и список аргументов. Но, как только функции-кандидаты и списки аргументов определены, выбор лучшей функции во всех случаях одинаков:

  • Сначала выбирается подмножество функций-кандидатов (те, которые имеют нужное количество аргументов и удовлетворяют некоторым другим условиям), чтобы сформировать набор жизнеспособных функций ([over.match.viable]).

  • Затем выбирается лучшая жизнеспособная функция на основе implicit conversion sequences необходимости сопоставления каждого аргумента с соответствующим параметром каждой жизнеспособной функции.

Если лучшая жизнеспособная функция существует и уникальна, разрешение перегрузки завершается успешно и в результате выдает ее. В противном случае разрешение перегрузки не удастся, и вызов будет неправильно сформирован. Когда разрешение перегрузки проходит успешно и лучшая жизнеспособная функция не находится accessible в контексте, в котором она используется, программа плохо сформирована.

16.3.1 Candidate functions and argument lists [over.match.funcs]

Подразделы [over.match.funcs] описывают набор функций-кандидатов и список аргументов, представленных для разрешения перегрузки в каждом из семи контекстов, в которых используется разрешение перегрузки. Исходные преобразования и конструкции, определенные в этих подпунктах, предназначены только для описания процесса разрешения перегрузки. Для использования таких преобразований и построений реализация не требуется.

Набор функций-кандидатов может содержать как функции-члены, так и функции, не являющиеся членами, которые должны быть сопоставлены с одним и тем же списком аргументов. Чтобы списки аргументов и параметров были сопоставимы в пределах этого гетерогенного набора, считается, что функция-член имеет дополнительный параметр, называемый implicit object parameter, который представляет объект, для которого была вызвана функция-член. В целях разрешения перегрузки как статические, так и нестатические функции-члены имеют неявный объектный параметр, а конструкторы - нет.

Аналогичным образом, когда это уместно, контекст может создать список аргументов, который содержит символ implied object argument для обозначения объекта, над которым нужно работать. Поскольку аргументы и параметры связаны по позиции в их соответствующих списках, соглашение заключается в том, что неявный параметр объекта, если он присутствует, всегда является первым параметром, а подразумеваемый аргумент объекта, если он присутствует, всегда является первым аргументом.

Для нестатических функций-членов тип неявного параметра объекта -

где X - класс, членом которого является функция, и cv cv-квалификация в объявлении функции-члена. [ Example: Для const функции-члена класса Xпредполагается, что дополнительный параметр имеет тип «ссылка на const X». ] Для функций преобразования функция считается членом класса аргумента неявного объекта с целью определения типа параметра неявного объекта. Для функций без преобразования, введенных a в производный класс, функция считается членом производного класса с целью определения типа неявного параметра объекта. Для статических функций-членов считается, что параметр неявного объекта соответствует любому объекту (поскольку, если функция выбрана, объект отбрасывается). [ Фактический тип не установлен для параметра неявного объекта статической функции-члена, и не будет предпринята попытка определить последовательность преобразования для этого параметра ( ). ]end exampleusing-declarationNote: [over.match.best]end note

Во время разрешения перегрузки подразумеваемый объектный аргумент неотличим от других аргументов. Однако неявный параметр объекта сохраняет свою идентичность, поскольку никакие пользовательские преобразования не могут применяться для достижения соответствия типа с ним. Для нестатических функций-членов, объявленных без a ref-qualifier, применяется дополнительное правило:

  • даже если параметр неявного объекта не является константным, значение rvalue может быть привязано к параметру, если во всех других отношениях аргумент может быть преобразован в тип параметра неявного объекта. [ Note: Тот факт, что такой аргумент является rvalue, не влияет на ranking последовательность неявных преобразований. ] end note

Поскольку кроме инициализации списка разрешено только одно определяемое пользователем преобразование в неявной последовательности преобразования, при выборе наилучшего определяемого пользователем преобразования применяются особые правила ([over.match.best], [over.best.ics]). [Example:

class T {
public:
  T();
};

class C : T {
public:
  C(int);
};
T a = 1;            // ill-formed: T(C(1)) not tried

end example]

В каждом случае, когда кандидат является шаблоном функции, специализации шаблона функции кандидата генерируются с использованием вывода аргументов шаблона ([temp.over], [temp.deduct]). Затем эти кандидаты рассматриваются как функции-кандидаты обычным образом.124 Данное имя может относиться к одному или нескольким шаблонам функций, а также к набору перегруженных нешаблонных функций. В таком случае функции-кандидаты, сгенерированные из каждого шаблона функции, объединяются с набором функций-кандидатов, не являющихся шаблоном.

Конструктор перемещения по умолчанию или оператор присваивания ([class.copy]), определенный как удаленный, исключается из набора функций-кандидатов во всех контекстах.

Процесс вывода аргументов полностью определяет типы параметров специализаций шаблонов функций, т. Е. Параметры специализаций шаблонов функций не содержат типов параметров шаблона. Следовательно, если не указано иное, специализации шаблонов функций и нешаблонные functions ([dcl.fct]) обрабатываются одинаково для оставшейся части разрешения перегрузки.

16.3.1.1 Function call syntax [over.match.call]

В function call

postfix-expression ( expression-listopt )

если postfix-expressionобозначает набор перегруженных функций и / или шаблонов функций, применяется разрешение перегрузки, как указано в [over.call.func]. Если postfix-expressionобозначает объект типа класса, применяется разрешение перегрузки, как указано в [over.call.object].

Если postfix-expressionобозначает адрес набора перегруженных функций и / или шаблонов функций, разрешение перегрузки применяется с использованием этого набора, как описано выше. Если функция, выбранная с помощью разрешения перегрузки, является нестатической функцией-членом, программа имеет неправильный формат. [ Note: Разрешение адреса перегрузки, установленного в других контекстах, описано в [over.over]. ] end note

16.3.1.1.1 Call to named function [over.call.func]

Интересны [over.call.func] только те вызовы функций, в которых в postfix-expression конечном итоге содержится имя, обозначающее одну или несколько функций, которые могут быть вызваны. Такой postfix-expression, возможно заключенный произвольно глубоко в круглые скобки, имеет одну из следующих форм:

postfix-expression:
	postfix-expression . id-expression
	postfix-expression -> id-expression
	primary-expression

Они представляют две синтаксические подкатегории вызовов функций: квалифицированные вызовы функций и неквалифицированные вызовы функций.

В квалифицированных вызовах функций разрешаемое имя - это оператор или, id-expression которому предшествует оператор. Поскольку конструкция обычно эквивалентна , остальная часть предложения предполагает без потери общности, что все вызовы функций-членов были нормализованы к форме, которая использует объект и оператор. Кроме того, Clause предполагает, что левый операнд оператора имеет тип « », где обозначает класс . При этом предположении, вызов в вызове рассматривается как функция-член следования правилам поиска имен в classes ( ). Объявления функций, найденные в результате этого поиска, составляют набор функций-кандидатов. Список аргументов в вызове дополнен добавлением левого операнда оператора в вызове нормализованной функции-члена в качестве подразумеваемого аргумента объекта ( ).->.A->B(*A).B [over] . [over] postfix-expression.cv TT125id-expressionT[class.member.lookup]expression-list.[over.match.funcs]

В неквалифицированных вызовах функций имя не уточняется оператором-> или .и имеет более общую форму primary-expression. Имя ищется в контексте вызова функции в соответствии с обычными правилами для name lookup вызовов функций. Объявления функций, найденные в результате этого поиска, составляют набор функций-кандидатов. Из-за правил поиска имени набор функций-кандидатов состоит (1) полностью из функций, не являющихся членами, или (2) полностью из функций-членов некоторого класса T. В случае (1) список аргументов такой же, как expression-list в вызове. В случае (2) список аргументов expression-list в вызове дополнен добавлением подразумеваемого аргумента объекта, как при квалифицированном вызове функции. Если ключевое слово this находится в области видимости и относится к классу Tили производному классу T, то подразумеваемым аргументом объекта является (*this). Если ключевое слово this не входит в область видимости или относится к другому классу, то надуманный объект типа T становится подразумеваемым аргументом объекта126. Если список аргументов дополняется надуманным объектом и при разрешении перегрузки выбирается одна из нестатических функций-членов T, вызов имеет неправильный формат.

Обратите внимание, что cv-квалификаторы для типа объектов важны для разрешения перегрузки как для объектов glvalue, так и для объектов класса prvalue.

Аргумент неявного объекта должен соответствовать параметру неявного объекта, присвоенному функциям-членам во время разрешения перегрузки. Он не используется при вызове выбранной функции. Поскольку все функции-члены имеют один и тот же неявный параметр объекта, надуманный объект не будет причиной выбора или отклонения функции.

16.3.1.1.2 Call to object of class type [over.call.object]

Если primary-expression E синтаксис вызова функции оценивается как объект класса типа «cv T», то набор функций-кандидатов включает в себя, по крайней мере, операторы вызова функций T. Операторы вызова функции T получаются обычным поиском имени operator() в контексте (E).operator().

Кроме того, для каждой неявной функции преобразования, объявленной в T форме

operator conversion-type-id () cv-qualifier ref-qualifieropt noexcept-specifieropt attribute-specifier-seqopt ;

где cv-qualifier такая же CV-квалификация, что и, или более высокая CV-квалификация, чем,, cvи где conversion-type-id обозначает тип «указатель на функциюP1,,Pnвозврата ( ) R», или тип «ссылка на указатель на функциюP1,,Pnвозврата ( ) R», или введите «ссылка на функциюP1,,Pnвозврата ( ) R», surrogate call function с уникальным именем call-function и имеющей форму

R call-function ( conversion-type-id  F, P1 a1, , Pn an) { return F (a1, , an); }

также рассматривается как функция-кандидат. Точно так же суррогатные функции вызова добавляются к набору функций-кандидатов для каждой неявной функции преобразования, объявленной в базовом классе, при T условии, что функция не скрыта внутри T другим промежуточным объявлением127.

Если такая суррогатная функция вызова выбрана разрешением перегрузки, соответствующая функция преобразования будет вызвана для преобразования E в соответствующий указатель функции или ссылку, а затем функция будет вызвана с аргументами вызова. Если функция преобразования не может быть вызвана (например, из-за двусмысленности), программа сформирована неправильно.

Список аргументов, представленный для разрешения перегрузки, состоит из выражений аргументов, присутствующих в синтаксисе вызова функции, которым предшествует подразумеваемый аргумент объекта (E). [ Note: При сравнении вызова с операторами вызова функции подразумеваемый аргумент объекта сравнивается с неявным параметром объекта оператора вызова функции. При сравнении вызова с суррогатной функцией вызова, подразумеваемый аргумент объекта сравнивается с первым параметром суррогатной функции вызова. Функция преобразования, из которой была получена суррогатная функция вызова, будет использоваться в последовательности преобразования для этого параметра, поскольку она преобразует подразумеваемый аргумент объекта в соответствующий указатель функции или ссылку, требуемую этим первым параметром. ] [end noteExample:

int f1(int);
int f2(float);
typedef int (*fp1)(int);
typedef int (*fp2)(float);
struct A {
  operator fp1() { return f1; }
  operator fp2() { return f2; }
} a;
int i = a(1);                   // calls f1 via pointer returned from conversion function

end example]

Обратите внимание, что эта конструкция может давать функции вызова-кандидата, которые нельзя отличить одна от другой по разрешению перегрузки, потому что они имеют идентичные объявления или отличаются только типом возвращаемого значения. Вызов будет неоднозначным, если при разрешении перегрузки не удается выбрать совпадение с вызовом, которое однозначно лучше, чем такие недифференцируемые функции.

16.3.1.2 Operators in expressions [over.match.oper]

Если ни один из операндов оператора в выражении не имеет типа, который является классом или перечислением, предполагается, что оператор является встроенным оператором и интерпретируется в соответствии с Clause [expr]. [ Note: Поскольку ., .*и ​::​ не могут быть перегружены, эти операторы всегда являются встроенными операторами, интерпретируемыми в соответствии с пунктом [expr]. ?: не может быть перегружен, но правила в этом подпункте используются для определения преобразований, которые будут применяться ко второму и третьему операндам, если они имеют тип класса или перечисления ([expr.cond]). ] [end noteExample:

struct String {
  String (const String&);
  String (const char*);
  operator const char* ();
};
String operator + (const String&, const String&);

void f() {
 const char* p= "one" + "two";  // ill-formed because neither operand has class or enumeration type
 int I = 1 + 1;                 // always evaluates to 2 even if class or enumeration types exist
                                // that would perform the operation.
}

end example]

Если какой-либо из операндов имеет тип, который является классом или перечислением, может быть объявлена ​​пользовательская операторная функция, реализующая этот оператор, или может потребоваться пользовательское преобразование для преобразования операнда в тип, подходящий для встроенного в операторе. В этом случае разрешение перегрузки используется, чтобы определить, какую операторную функцию или встроенный оператор следует вызывать для реализации оператора. Следовательно, обозначение оператора сначала преобразуется в эквивалентное обозначение вызова функции, как показано в таблице 12 (где @ обозначает один из операторов, описанных в указанном подпункте). Однако операнды располагаются в порядке, установленном для встроенного оператора (пункт [expr]).

Таблица 12 - Связь между оператором и обозначением вызова функции
Подпункт Выражение Как функция-член Как функция, не являющаяся членом
[over.unary] @a (a).operator@ () operator@(a)
[over.binary] a@b (a).operator@ (b) operator@(a, b)
[over.ass] a=b (a).operator= (b)
[over.sub] a[b] (a).operator[](b)
[over.ref] a-> (a).operator->()
[over.inc] a@ (a).operator@ (0) operator@(a, 0)

Для унарного оператора @ с операндом типа, чья cv-неквалифицированная версия равна T1, и для бинарного оператора @ с левым операндом типа, чья cv-неквалифицированная версия есть, T1 и правым операндом типа, чья cv-неквалифицированная версия равна T2, три наборы кандидатов функций, обозначены member candidates, non-member candidates и built-in candidates, строятся следующим образом :

  • Если T1 это полный тип класса или класс, определяемый в настоящее время, набор кандидатов в члены является результатом квалифицированного поиска T1​::​operator@ ([over.call.func]); в противном случае набор кандидатов в члены пуст.

  • Набор кандидатов, не являющихся членами, является результатом неквалифицированного поиска operator@ в контексте выражения в соответствии с обычными правилами поиска имени в неквалифицированных вызовах функций ([basic.lookup.argdep]), за исключением того, что все функции-члены игнорируются. Однако, если ни один операнд не имеет типа класса, только те функции, не являющиеся членами в наборе подстановки, которые имеют первый параметр типа T1 или «ссылку на cv T1», когда T1 это тип перечисления или (если есть правый операнд) второй Параметр типа T2 или «ссылка на cv T2», когда T2 является типом перечисления, являются функциями-кандидатами.

  • Для оператора ,, унарного оператора &или оператора ->набор встроенных кандидатов пуст. Для всех других операторов встроенные кандидаты включают в себя все функции операторов-кандидатов, определенные в [over.built] этом, по сравнению с данным оператором,

    • иметь то же имя оператора и

    • принять такое же количество операндов и

    • принимать типы операндов, в которые можно преобразовать данный операнд или операнды в соответствии с [over.best.ics], и

    • не имеют того же списка типов параметров, что и любой кандидат, не являющийся членом, который не является специализацией шаблона функции.

Для встроенных операторов присваивания преобразование левого операнда ограничено следующим образом:

  • не вводятся временные файлы для хранения левого операнда, и

  • к левому операнду не применяются определяемые пользователем преобразования для достижения соответствия типа самому левому параметру встроенного кандидата.

Для всех остальных операторов такие ограничения отсутствуют.

Набор функций-кандидатов для разрешения перегрузки - это объединение кандидатов в члены, кандидатов, не являющихся членами, и встроенных кандидатов. Список аргументов содержит все операнды оператора. Лучшая функция из набора функций-кандидатов выбирается в соответствии с [over.match.viable] и [over.match.best].128 [Example:

struct A {
  operator int();
};
A operator+(const A&, const A&);
void m() {
  A a, b;
  a + b;                        // operator+(a, b) chosen over int(a) + int(b)
}

end example]

Если встроенный кандидат выбран путем разрешения перегрузки, операнды типа класса преобразуются в типы соответствующих параметров выбранной функции операции, за исключением того, что вторая стандартная последовательность преобразования a user-defined conversion sequence не применяется. Затем оператор рассматривается как соответствующий встроенный оператор и интерпретируется в соответствии с пунктом [expr]. [Example:

struct X {
  operator double();
};

struct Y {
  operator int*();
};

int *a = Y() + 100.0;           // error: pointer arithmetic requires integral operand
int *b = Y() + X();             // error: pointer arithmetic requires integral operand

end example]

Второй операнд оператора -> игнорируется при выборе operator-> функции и не является аргументом при operator-> вызове функции. При operator-> возврате оператор -> применяется к возвращенному значению с исходным вторым операндом.129

Если оператор является оператором ,, унарным оператором &или оператором ->и нет жизнеспособных функций, то предполагается, что оператор является встроенным оператором и интерпретируется в соответствии с пунктом [expr].

[ Note: Правила поиска для операторов в выражениях отличаются от правил поиска для имен операторных функций в вызове функции, как показано в следующем примере:

struct A { };
void operator + (A, A);

struct B {
  void operator + (B);
  void f ();
};

A a;

void B::f() {
  operator+ (a,a);              // error: global operator hidden by member
  a + a;                        // OK: calls global operator+
}

end note]

Если набор функций-кандидатов пуст, разрешение перегрузки не выполняется.

Если значение, возвращаемое operator-> функцией, имеет тип класса, это может привести к выбору и вызову другой operator-> функции. Процесс повторяется до тех пор, пока operator-> функция не вернет значение неклассового типа.

16.3.1.3 Initialization by constructor [over.match.ctor]

Когда объекты типа класса direct-initializedинициализируются копией из выражения того же или производного типа класса ([dcl.init]), либо при default-initializedразрешении перегрузки выбирается конструктор. Для прямой инициализации или инициализации по умолчанию, которые не находятся в контексте инициализации копирования, все функции-кандидаты являются конструкторами класса инициализируемого объекта. Для инициализации копирования все функции-кандидаты converting constructors принадлежат этому классу. Список аргументов - это expression-listили assignment-expression из initializer.

16.3.1.4 Copy-initialization of class by user-defined conversion [over.match.copy]

В условиях, указанных в [dcl.init], как часть инициализации копии объекта типа класса, может быть вызвано определяемое пользователем преобразование для преобразования выражения инициализатора в тип инициализируемого объекта. Разрешение перегрузки используется для выбора вызываемого пользователем преобразования. [ Note: Преобразование, выполняемое для косвенной привязки к ссылке на тип класса, возможно, квалифицированный cv, определяется в терминах соответствующей инициализации копии без ссылки. ] Предполагая, что « » является типом инициализируемого объекта, с типом класса, функции-кандидаты выбираются следующим образом: end notecv1 TT

  • converting constructors Из T являются функциями кандидатов.

  • Когда тип выражения инициализатора является типом класса «cv S», рассматриваются неявные функции преобразования S и его базовые классы. При инициализации временного объекта, который должен быть привязан к первому параметру конструктора, где параметр имеет тип «ссылка на возможно- cvквалифицированный T», а конструктор вызывается с одним аргументом в контексте прямой инициализации объекта типа «cv2 T» , также рассматриваются явные функции преобразования. Те, которые не скрыты внутри S и дают тип, чья cv-неквалифицированная версия является тем же типом, что T и его производный класс, являются функциями-кандидатами. Функции преобразования, которые возвращают «ссылку на X», возвращают lvalue или xvalue, в зависимости от типа ссылки, типа X и поэтому считаются X подходящими для этого процесса выбора функций-кандидатов.

В обоих случаях список аргументов имеет один аргумент, который является выражением инициализатора. [ Note: Этот аргумент будет сравниваться с первым параметром конструкторов и неявным параметром объекта функций преобразования. ]end note

16.3.1.5 Initialization by conversion function [over.match.conv]

В условиях, указанных в [dcl.init], как часть инициализации объекта неклассового типа, функция преобразования может быть вызвана для преобразования выражения инициализатора типа класса в тип инициализируемого объекта. Разрешение перегрузки используется для выбора вызываемой функции преобразования. Предполагая, что «cv1 T» - это тип инициализируемого объекта, а «cv S» - это тип выражения инициализатора, с S типом класса функции-кандидаты выбираются следующим образом:

  • Рассмотрены функции преобразования S и его базовые классы. Те неявные функции преобразования, которые не скрыты внутри S и дают тип T или тип, который может быть преобразован в тип с T помощью a, standard conversion sequence являются функциями-кандидатами. Для прямой инициализации те явные функции преобразования, которые не скрыты внутри S и дают тип T или тип, который может быть преобразован в тип T с помощью a qualification conversion , также являются функциями-кандидатами. Считается, что функции преобразования, возвращающие тип с квалификацией cv, дают неквалифицированную версию этого типа для этого процесса выбора функций-кандидатов. Функции преобразования, которые возвращают «ссылку на cv2 X», возвращают значения l или x, в зависимости от типа ссылки, типа «cv2 X» и, следовательно, считаются X подходящими для этого процесса выбора функций-кандидатов.

Список аргументов имеет один аргумент, который является выражением инициализатора. [ Note: Этот аргумент будет сравниваться с неявным параметром объекта функций преобразования. ]end note

16.3.1.6 Initialization by conversion function for direct reference binding [over.match.ref]

В условиях, указанных в [dcl.init.ref], ссылка может быть связана непосредственно с glvalue или классом prvalue, который является результатом применения функции преобразования к выражению инициализатора. Разрешение перегрузки используется для выбора вызываемой функции преобразования. Предполагая, что «ссылка на cv1 T» - это тип инициализируемой ссылки, а «cv S» - это тип выражения инициализатора, с S типом класса функции-кандидаты выбираются следующим образом:

  • Рассмотрены функции преобразования S и его базовые классы. Те неявные функции преобразования, которые не скрыты внутри S и дают тип «ссылка lvalue на cv2 T2» (при инициализации ссылки lvalue или ссылки rvalue на функцию) или «cv2 T2» или «ссылка rvalue на cv2 T2» (при инициализации ссылки rvalue или lvalue ссылка на функцию), где «cv1 T» стоит reference-compatible с «cv2 T2», являются функциями-кандидатами. Для прямой инициализации те явные функции преобразования, которые не скрыты внутри S и дают тип «ссылка lvalue на cv2 T2» или «cv2 T2» или «ссылка rvalue на cv2 T2», соответственно, где T2 тот же тип T или может быть преобразован в тип T с a qualification conversion, являются также кандидатские функции.

Список аргументов имеет один аргумент, который является выражением инициализатора. [ Note: Этот аргумент будет сравниваться с неявным параметром объекта функций преобразования. ]end note

16.3.1.7 Initialization by list-initialization [over.match.list]

Когда объекты неагрегированного типа класса T инициализируются списком, что [dcl.init.list] указывает, что разрешение перегрузки выполняется в соответствии с правилами в этом разделе, разрешение перегрузки выбирает конструктор в два этапа:

  • Первоначально функции-кандидаты являются конструкторами списка инициализаторов ([dcl.init.list]) класса, T а список аргументов состоит из списка инициализаторов в виде единственного аргумента.

  • Если жизнеспособный конструктор списка инициализаторов не найден, снова выполняется разрешение перегрузки, где все функции-кандидаты являются конструкторами класса, T а список аргументов состоит из элементов списка инициализаторов.

Если список инициализаторов не имеет элементов и T имеет конструктор по умолчанию, первая фаза опускается. При инициализации списка копирования, если explicit выбран конструктор, инициализация имеет неправильный формат . [ Note: Это отличается от других ситуаций ([over.match.ctor], [over.match.copy]), где для инициализации копирования рассматриваются только конструкторы преобразования. Это ограничение применяется только в том случае, если эта инициализация является частью окончательного результата разрешения перегрузки. ] end note

16.3.1.8 Class template argument deduction [over.match.class.deduct]

Формируется набор функций и шаблонов функций, включающий:

  • Для каждого конструктора шаблона первичного класса, обозначенного template-name, если шаблон определен, шаблон функции со следующими свойствами:

    • Параметры шаблона - это параметры шаблона шаблона класса, за которыми следуют параметры шаблона (включая аргументы шаблона по умолчанию) конструктора, если таковые имеются.

    • Типы параметров функции такие же, как у конструктора.

    • Тип возвращаемого значения - это специализация шаблона класса, обозначенная template-name аргументами и шаблона, соответствующими параметрам шаблона, полученным из шаблона класса.

  • Если шаблон основного класса C не определен или не объявляет никаких конструкторов, дополнительный шаблон функции, полученный, как указано выше, из гипотетического конструктора C().

  • Дополнительный шаблон функции, полученный, как указано выше, из гипотетического конструктора C(C), который называется copy deduction candidate.

  • Для каждого из них deduction-guide- шаблон функции или функции со следующими свойствами:

Инициализация и разрешение перегрузки выполняются, как описано в [dcl.init] и [over.match.ctor],, [over.match.copy]или [over.match.list] (в зависимости от типа выполняемой инициализации) для объекта гипотетического типа класса, где выбранные функции и шаблоны функций считаются конструкторами этого типа класса для цель формирования набора перегрузки, а инициализатор предоставляется контекстом, в котором было выполнено вывод аргументов шаблона класса. Каждый такой условный конструктор считается явным, если функция или шаблон функции были сгенерированы из конструктора или deduction-guideбыли объявлены explicit. Все такие условные конструкторы считаются открытыми членами гипотетического типа класса.

[Example:

template <class T> struct A {
  explicit A(const T&, ...) noexcept;  // #1
  A(T&&, ...);                         // #2
};

int i;
A a1 = { i, i };    // error: explicit constructor #1 selected in copy-list-initialization during deduction,
                    // cannot deduce from non-forwarding rvalue reference in #2

A a2{i, i};         // OK, #1 deduces to A<int> and also initializes
A a3{0, i};         // OK, #2 deduces to A<int> and also initializes
A a4 = {0, i};      // OK, #2 deduces to A<int> and also initializes

template <class T> A(const T&, const T&) -> A<T&>;  // #3
template <class T> explicit A(T&&, T&&) -> A<T>;    // #4

A a5 = {0, 1};      // error: explicit deduction guide #4 selected in copy-list-initialization during deduction
A a6{0,1};          // OK, #4 deduces to A<int> and #2 initializes
A a7 = {0, i};      // error: #3 deduces to A<int&>, #1 and #2 declare same constructor
A a8{0,i};          // error: #3 deduces to A<int&>, #1 and #2 declare same constructor

template <class T> struct B {
  template <class U> using TA = T;
  template <class U> B(U, TA<U>);
};

B b{(int*)0, (char*)0};         // OK, deduces B<char*>

end example]

16.3.2 Viable functions [over.match.viable]

Из набора функций-кандидатов, созданных для данного context ([over.match.funcs]), выбирается набор жизнеспособных функций, из которых будет выбрана лучшая функция путем сравнения последовательностей преобразования аргументов для наилучшего соответствия ([over.match.best]). При выборе жизнеспособных функций учитываются отношения между аргументами и параметрами функции, кроме ранжирования последовательностей преобразования.

Во-первых, чтобы быть жизнеспособной функцией, функция-кандидат должна иметь достаточно параметров, чтобы соответствовать количеству аргументов в списке.

  • Еслиm в списке есть аргументы, все функции-кандидаты, имеющие точно m параметры, являются жизнеспособными.

  • Функция-кандидат, имеющая меньше m параметров, жизнеспособна, только если в ее списке параметров есть многоточие ([dcl.fct]). В целях разрешения перегрузки любой аргумент, для которого нет соответствующего параметра, считается «совпадающим с многоточием» ([over.ics.ellipsis]).

  • Функция-кандидат, имеющая болееm параметров, жизнеспособна, только если (m+1)-st параметр имеет расширение default argument.130 В целях разрешения перегрузки список параметров усечен справа, так что есть ровно m параметры.

Во-вторых, F чтобы быть жизнеспособной функцией, для каждого аргумента должен существовать объект, implicit conversion sequence который преобразует этот аргумент в соответствующий параметр F. Если параметр имеет ссылочный тип, неявная последовательность преобразования включает в себя операцию привязки ссылки, и тот факт, что ссылка lvalue на non-const не может быть привязана к rvalue и что ссылка rvalue не может быть привязана к lvalue, может повлиять на жизнеспособность функции (см. [over.ics.ref]).

Согласно [dcl.fct.default], параметры, следующие за (m+1)-st параметром, также должны иметь аргументы по умолчанию.

16.3.3 Best viable function [over.match.best]

Определите ICSi(F) следующим образом:

  • Если F это статическая функция-член, ICS1(F) определяется таким образом, что ICS1(F) не лучше и не хуже, чем ICS1(G) для любой функции G, и симметрично ICS1(G) не лучше и не хуже, чем ICS1(F);131 иначе,

  • пусть ICSi(F) обозначает неявную последовательность преобразования, которая преобразует i-й аргумент в списке в тип i-й параметр жизнеспособной функции F. [over.best.ics] определяет последовательности неявного преобразования и [over.ics.rank] определяет, что означает, что одна последовательность неявного преобразования является лучшей последовательностью преобразования или худшей последовательностью преобразования, чем другая.

С учетом этих определений жизнеспособная функция F1 определяется как better функция, чем другая жизнеспособная функция, F2 если для всех аргументов iICSi(F1) не является худшей последовательностью преобразования, чем ICSi(F2), а затем

  • для некоторого аргумента jICSj(F1) является лучшей последовательностью преобразования, чем ICSj(F2), или, если это не так,

  • контекст - это инициализация посредством определяемого пользователем преобразования (см. [dcl.init], [over.match.conv]и [over.match.ref]), а стандартная последовательность преобразования из возвращаемого типа F1 в целевой тип (т. е. тип инициализируемой сущности) является лучшей последовательностью преобразования, чем стандартное преобразование. последовательность от типа возврата F2 к типу назначения [Example:

    struct A {
      A();
      operator int();
      operator double();
    } a;
    int i = a;          // a.operator int() followed by no conversion is better than
                        // a.operator double() followed by a conversion to int
    float x = a;        // ambiguous: both possibilities require conversions,
                        // and neither is better than the other
    

    end example] или, если не это,

  • контекст - это инициализация с помощью функции преобразования для direct reference binding ссылки на тип функции, возвращаемый тип F1 - это тот же вид ссылки (т.е. lvalue или rvalue), что и инициализируемая ссылка, а возвращаемый тип F2 - не [Example:

    template <class T> struct A {
      operator T&();    // #1
      operator T&&();   // #2
    };
    typedef int Fn();
    A<Fn> a;
    Fn& lf = a;         // calls #1
    Fn&& rf = a;        // calls #2
    

    end example] или, если не это,

  • F1 не является специализацией шаблона функции, а F2 является специализацией шаблона функции, или, если это не так,

  • F1 и F2 являются специализациями шаблонов функций, а шаблон функции для F1 более специализирован, чем шаблон для, в F2 соответствии с правилами частичного упорядочивания, описанными в [temp.func.order], или, если это не так,

  • F1 генерируется из a deduction-guide([over.match.class.deduct]) и F2 не является, или, если это не так,

  • F1 является кандидатом на вычитание копии ([over.match.class.deduct]) и F2 не является, или, если это не так,

  • F1 создается из конструктора, не являющегося шаблоном, и F2 создается из шаблона конструктора. [Example:

    template <class T> struct A {
      using value_type = T;
      A(value_type);    // #1
      A(const A&);      // #2
      A(T, T, int);     // #3
      template<class U>
        A(int, T, U);   // #4
      // #5 is the copy deduction candidate, A(A)
    };
    
    A x(1, 2, 3);       // uses #3, generated from a non-template constructor
    
    template <class T>
    A(T) -> A<T>;       // #6, less specialized than #5
    
    A a(42);            // uses #6 to deduce A<int> and #1 to initialize
    A b = a;            // uses #5 to deduce A<int> and #2 to initialize
    
    template <class T>
    A(A<T>) -> A<A<T>>; // #7, as specialized as #5
    
    A b2 = a;           // uses #7 to deduce A<A<int>> and #1 to initialize
    

    end example]

Если существует ровно одна жизнеспособная функция, которая является лучшей функцией, чем все другие жизнеспособные функции, то она выбирается с помощью разрешения перегрузки; в противном случае звонок будет неправильно сформирован.132 [Example:

void Fcn(const int*,  short);
void Fcn(int*, int);

int i;
short s = 0;

void f() {
  Fcn(&i, s);       // is ambiguous because &i  int* is better than &i  const int*
                    // but s  short is also better than s  int

  Fcn(&i, 1L);      // calls Fcn(int*, int), because &i  int* is better than &i  const int*
                    // and 1L  short and 1L  int are indistinguishable

  Fcn(&i, 'c');     // calls Fcn(int*, int), because &i  int* is better than &i  const int*
                    // and c  int is better than c  short
}

end example]

Если лучшая жизнеспособная функция разрешается в функцию, для которой было найдено несколько объявлений, и если по крайней мере два из этих объявлений - или объявления, на которые они ссылаются, в случае using-declarations - указывают аргумент по умолчанию, который сделал функцию жизнеспособной, программа плохая. -формированный. [Example:

namespace A {
  extern "C" void f(int = 5);
}
namespace B {
  extern "C" void f(int = 5);
}

using A::f;
using B::f;

void use() {
  f(3);             // OK, default argument was not used for viability
  f();              // Error: found default argument twice
}

end example]

Если функция является статической функцией-членом, это определение означает, что первый аргумент, подразумеваемый аргумент объекта, не влияет на определение того, является ли функция лучше или хуже любой другой функции.

Алгоритм выбора наилучшей жизнеспособной функции линейен по количеству жизнеспособных функций. Проведите простой турнир, чтобы найти функцию, W которая не хуже любого соперника, с которым она столкнулась. Хотя другая функция F , с W которой не сталкивались, могла быть по крайней мере так же хороша, как W, F не может быть лучшей функцией, потому что в какой-то момент турнира F встретилась другая функция, G такая F как G. Следовательно,W это либо лучшая функция , либо лучшая функция не существует. Итак, сделайте второй проход по жизнеспособным функциям, чтобы убедиться, что они W лучше, чем все другие функции.

16.3.3.1 Implicit conversion sequences [over.best.ics]

An - это последовательность преобразований, используемая для преобразования аргумента в вызове функции в тип соответствующего параметра вызываемой функции. Последовательность преобразований является неявным преобразованием, как определено в разделе , что означает, что она регулируется правилами инициализации объекта или ссылки одним выражением ( , ).implicit conversion sequence [conv][dcl.init] [dcl.init.ref]

Последовательности неявного преобразования связаны только с типом, cv-квалификацией и категорией значения аргумента, а также с тем, как они преобразуются для соответствия соответствующим свойствам параметра. Другие свойства, такие как время жизни, класс хранения, выравнивание, доступность аргумента, является ли аргумент битовым полем и является ли функция deleted, игнорируются. Таким образом, хотя неявная последовательность преобразования может быть определена для данной пары аргумент-параметр, преобразование аргумента в параметр может по-прежнему быть плохо сформированным в окончательном анализе.

Правильно сформированная последовательность неявного преобразования - это одна из следующих форм:

Однако если цель

  • первый параметр конструктора или

  • неявный объектный параметр определяемой пользователем функции преобразования

а конструктор или определяемая пользователем функция преобразования является кандидатом от

  • [over.match.ctor], когда аргумент является временным на втором этапе инициализации копии класса,

  • [over.match.copy],, [over.match.conv]или [over.match.ref] (во всех случаях), или

  • вторая фаза, [over.match.list] когда список инициализаторов имеет ровно один элемент, который сам является списком инициализаторов, а цель является первым параметром конструктора класса X, а преобразование - в X или ссылка на cv X,

определяемые пользователем последовательности преобразования не рассматриваются. [ Note: Эти правила предотвращают применение более одного определяемого пользователем преобразования во время разрешения перегрузки, тем самым избегая бесконечной рекурсии. ] [end noteExample:

  struct Y { Y(int); };
  struct A { operator int(); };
  Y y1 = A();       // error: A​::​operator int() is not a candidate

  struct X { };
  struct B { operator X(); };
  B b;
  X x({b});         // error: B​::​operator X() is not a candidate

end example]

Для случая, когда тип параметра является ссылкой, см [over.ics.ref]..

Если тип параметра не является ссылкой, неявная последовательность преобразования моделирует копирование-инициализацию параметра из выражения аргумента. Неявная последовательность преобразования - это последовательность, необходимая для преобразования выражения аргумента в значение типа параметра. [ Note: Если параметр имеет тип класса, это концептуальное преобразование, определенное для целей пункта [over]; фактическая инициализация определяется в терминах конструкторов и не является преобразованием. ] Любое различие в CV-квалификации верхнего уровня относится к самой инициализации и не является преобразованием. [ Параметр типа может быть инициализирован из аргумента типа . Последовательность неявного преобразования для этого случая является последовательностью идентичности; он не содержит «преобразования» из в . ] Когда параметр имеет тип класса и выражение аргумента имеет тот же тип, неявная последовательность преобразования является преобразованием идентичности. Когда параметр имеет тип класса, а выражение аргумента имеет тип производного класса, неявная последовательность преобразования представляет собой преобразование производного в базовый класс из производного класса в базовый класс. [ Такого стандартного преобразования нет; это преобразование производного в базовое существует только в описании неявных последовательностей преобразования. ] Производное преобразование в базовое имеет ранг преобразования ( ).end noteExample: Aconst Aconst AAend exampleNote: end note[over.ics.scs]

Во всех контекстах при преобразовании в параметр неявного объекта или при преобразовании в левый операнд операции присваивания разрешены только стандартные последовательности преобразования.

Если для сопоставления аргумента с типом параметра не требуется никаких преобразований, неявная последовательность преобразования - это стандартная последовательность преобразования, состоящая из преобразования идентичности ([over.ics.scs]).

Если не удается найти последовательность преобразований для преобразования аргумента в тип параметра, неявная последовательность преобразований не может быть сформирована.

Если существует несколько различных последовательностей преобразований, каждая из которых преобразует аргумент в тип параметра, неявная последовательность преобразования, связанная с параметром, определяется как уникальная последовательность преобразования, обозначенная как ambiguous conversion sequence. С целью ранжирования последовательностей неявных преобразований, как описано в [over.ics.rank]разделе, неоднозначная последовательность преобразования рассматривается как заданная пользователем последовательность преобразования, неотличимая от любой другой заданной пользователем последовательности преобразования. [ Note: Это правило предотвращает невозможность работы функции из-за неоднозначной последовательности преобразования одного из ее параметров. [Example:

class B;
class A { A (B&);};
class B { operator A (); };
class C { C (B&); };
void f(A) { }
void f(C) { }
B b;
f(b);               // ill-formed: ambiguous because there is a conversion b  C (via constructor)
                    // and an (ambiguous) conversion b  A (via constructor or conversion function)
void f(B) { }
f(b);               // OK, unambiguous

end example] ] Если функция, использующая неоднозначную последовательность преобразования, выбрана как наиболее жизнеспособная функция, вызов будет неправильно сформирован, поскольку преобразование одного из аргументов в вызове неоднозначно.end note

Три формы неявных последовательностей преобразования, упомянутые выше, определены в следующих подпунктах.

16.3.3.1.1 Standard conversion sequences [over.ics.scs]

Таблица 13 суммирует преобразования, определенные в пункте, [conv] и разбивает их на четыре непересекающиеся категории: преобразование Lvalue, корректировка квалификации, продвижение и преобразование. [ Note: Эти категории ортогональны по отношению к категории значения, cv-квалификации и представлению данных: преобразования Lvalue не изменяют cv-квалификацию или представление данных типа; Квалификационные корректировки не изменяют категорию значения или представление данных типа; и «Акции» и «Конверсии» не изменяют категорию значения или CV-квалификацию типа. ]end note

[ Note: Как описано в пункте [conv], стандартная последовательность преобразования представляет собой либо само преобразование идентификатора (то есть без преобразования), либо состоит из одного-трех преобразований из других четырех категорий. Если в последовательности есть два или более преобразований, преобразования применяются в каноническом порядке: преобразование Lvalue , продвижение или преобразование , корректировка квалификации . ]end note

Каждой конверсии в таблице 13 также соответствует ранг (точное соответствие, продвижение или конверсия). К ним привыкли rank standard conversion sequences. Ранг последовательности преобразования определяется путем рассмотрения ранга каждого преобразования в последовательности и ранга любого из них reference binding. Если любой из них имеет ранг конверсии, последовательность имеет ранг конверсии; в противном случае, если какой-либо из них имеет рейтинг повышения, последовательность имеет рейтинг повышения; в противном случае последовательность имеет ранг точного соответствия.

Таблица 13 - Конверсии
Преобразование Категория Классифицировать Подпункт
Никаких преобразований не требуется Личность
Lvalue-to-rvalue преобразование [conv.lval]
Преобразование массива в указатель Преобразование Lvalue [conv.array]
Преобразование функции в указатель Точное совпадение [conv.func]
Квалификационные преобразования [conv.qual]
Преобразование указателя на функцию Квалификационная корректировка [conv.fctptr]
Интегральные акции [conv.prom]
Продвижение с плавающей запятой Продвижение Продвижение [conv.fpprom]
Интегральные преобразования [conv.integral]
Преобразования с плавающей запятой [conv.double]
Преобразования с плавающей запятой [conv.fpint]
Преобразования указателей Преобразование Преобразование [conv.ptr]
Указатель на преобразования членов [conv.mem]
Логические преобразования [conv.bool]

16.3.3.1.2 User-defined conversion sequences [over.ics.user]

Определяемая пользователем последовательность преобразования состоит из начальной стандартной последовательности преобразования, за которой следует определяемое пользователем преобразование ([class.conv]), за которым следует вторая стандартная последовательность преобразования. Если определяемое пользователем преобразование равно specified by a constructor, начальная стандартная последовательность преобразования преобразует исходный тип в тип, требуемый аргументом конструктора. Если определяемое пользователем преобразование указано с помощью conversion function, начальная стандартная последовательность преобразования преобразует исходный тип в неявный объектный параметр функции преобразования.

Вторая стандартная последовательность преобразования преобразует результат определенного пользователем преобразования в целевой тип последовательности. Поскольку неявная последовательность преобразования является инициализацией, при выборе наилучшего пользовательского преобразования для пользовательской последовательности преобразования применяются специальные правила инициализации с помощью пользовательского преобразования (см. [over.match.best] И [over.best.ics]).

Если определяемое пользователем преобразование определяется специализацией шаблона функции преобразования, вторая стандартная последовательность преобразования должна иметь ранг точного совпадения.

Преобразованию выражения типа класса в тот же тип класса дается ранг точного соответствия, а преобразованию выражения типа класса в базовый класс этого типа дается ранг преобразования, несмотря на то, что конструктор (т. Е. , определяемая пользователем функция преобразования) вызывается для этих случаев.

16.3.3.1.3 Ellipsis conversion sequences [over.ics.ellipsis]

Последовательность преобразования многоточия возникает, когда аргумент в вызове функции совпадает со спецификацией параметра многоточия вызываемой функции (см [expr.call]. Раздел Ресурсы ).

16.3.3.1.4 Reference binding [over.ics.ref]

Когда параметр ссылочного типа binds directly на выражение аргумента, неявная последовательность преобразования является преобразованием идентичности, если только выражение аргумента не имеет тип, который является производным классом типа параметра, и в этом случае последовательность неявного преобразования является производным преобразованием в- базовое преобразование ([over.best.ics]). [Example:

struct A {};
struct B : public A {} b;
int f(A&);
int f(B&);
int i = f(b);       // calls f(B&), an exact match, rather than f(A&), a conversion

end example] Если параметр привязывается непосредственно к результату применения функции преобразования к выражению аргумента, неявная последовательность преобразования - это a user-defined conversion sequence, а вторая стандартная последовательность преобразования - либо преобразование идентичности, либо, если функция преобразования возвращает объект типа, который является производный класс типа параметра, преобразование производного в базовое.

Когда параметр ссылочного типа не привязан напрямую к выражению аргумента, последовательность преобразования - это та последовательность, которая требуется для преобразования выражения аргумента в ссылочный тип в соответствии с [over.best.ics]. Концептуально эта последовательность преобразования соответствует копированию-инициализации временного объекта указанного типа с помощью выражения аргумента. Любое различие в CV-квалификации верхнего уровня учитывается самой инициализацией и не является преобразованием.

За исключением неявного параметра объекта, о котором см. [over.match.funcs], Стандартная последовательность преобразования не может быть сформирована, если она требует привязки ссылки lvalue, отличной от ссылки на энергонезависимый const тип, к rvalue или привязки ссылки rvalue к lvalue, отличному от функции lvalue. [ Note: Это означает, например, что функция-кандидат не может быть жизнеспособной функцией, если она имеетconst параметр ссылки, отличного от lvalue (кроме неявного параметра объекта), и соответствующий аргумент потребует создания временного элемента для инициализации ссылки lvalue ( см. [dcl.init.ref]). ]end note

Однако другие ограничения на привязку ссылки к конкретному аргументу, не основанные на типах ссылки и аргумента, не влияют на формирование стандартной последовательности преобразования. [ Example: Функция с параметром «lvalue ссылка на int» может быть жизнеспособным кандидатом, даже если соответствующий аргумент является int битовым полем. Формирование последовательностей неявного преобразования обрабатывает int битовое поле как int lvalue и находит точное совпадение с параметром. Если функция выбрана путем разрешения перегрузки, вызов, тем не менее, будет неправильно сформирован из-за запрета на привязкуconst ссылки, отличной от lvalue, к битовому полю ([dcl.init.ref]). ]end example

16.3.3.1.5 List-initialization sequence [over.ics.list]

Когда аргумент является списком инициализатора ([dcl.init.list]), это не выражение, и для его преобразования в тип параметра применяются специальные правила.

Если тип параметра является агрегатным классом, X а список инициализаторов имеет единственный элемент типа cv U, где U is X или класс, производный от X, неявная последовательность преобразования - это последовательность, необходимая для преобразования элемента в тип параметра.

В противном случае, если тип параметра является символьным массивом,133 а в списке инициализаторов есть единственный элемент, который является строковым литералом соответствующего типа ([dcl.init.string]), неявная последовательность преобразования является преобразованием идентичности.

В противном случае, если тип параметра равен std​::​initializer_­list<X> и все элементы списка инициализатора могут быть неявно преобразованы в X, последовательность неявного преобразования является наихудшим преобразованием, необходимым для преобразования элемента списка в X, или, если список инициализатора не имеет элементов, идентификатор конверсия. Это преобразование может быть определенным пользователем преобразованием даже в контексте вызова конструктора списка инициализаторов. [Example:

void f(std::initializer_list<int>);
f( {} );                // OK: f(initializer_­list<int>) identity conversion
f( {1,2,3} );           // OK: f(initializer_­list<int>) identity conversion
f( {'a','b'} );         // OK: f(initializer_­list<int>) integral promotion
f( {1.0} );             // error: narrowing

struct A {
  A(std::initializer_list<double>);             // #1
  A(std::initializer_list<complex<double>>);    // #2
  A(std::initializer_list<std::string>);        // #3
};
A a{ 1.0,2.0 };         // OK, uses #1

void g(A);
g({ "foo", "bar" });    // OK, uses #3

typedef int IA[3];
void h(const IA&);
h({ 1, 2, 3 });         // OK: identity conversion

end example]

В противном случае, если тип параметра - «массив из N X», если существует неявная последовательность преобразования для каждого элемента массива из соответствующего элемента списка инициализаторов (или из, {} если такого элемента нет), последовательность неявного преобразования является худшая такая последовательность неявного преобразования.

В противном случае, если параметр не является совокупным учащимся X и Разрешением перегрузки PER [over.match.list] выбирает один лучший конструктором C из X выполнить инициализацию объекта типа X от аргумента инициализатора списка:

  • Если C это не конструктор списка инициализаторов, а список инициализаторов имеет единственный элемент типа cv U, где U is X или класс, производный от X, неявная последовательность преобразования имеет ранг точного соответствия, если он U есть X, или ранг преобразования, если U является производным от X.

  • В противном случае неявная последовательность преобразования является пользовательской последовательностью преобразования, а вторая стандартная последовательность преобразования является преобразованием идентичности.

Если жизнеспособны несколько конструкторов, но ни один из них не лучше других, последовательность неявного преобразования является последовательностью неоднозначного преобразования. Пользовательские преобразования разрешены для преобразования элементов списка инициализаторов в типы параметров конструктора, за исключением случаев, указанных в [over.best.ics]. [Example:

struct A {
  A(std::initializer_list<int>);
};
void f(A);
f( {'a', 'b'} );        // OK: f(A(std​::​initializer_­list<int>)) user-defined conversion

struct B {
  B(int, double);
};
void g(B);
g( {'a', 'b'} );        // OK: g(B(int, double)) user-defined conversion
g( {1.0, 1.0} );        // error: narrowing

void f(B);
f( {'a', 'b'} );        // error: ambiguous f(A) or f(B)

struct C {
  C(std::string);
};
void h(C);
h({"foo"});             // OK: h(C(std​::​string("foo")))

struct D {
  D(A, C);
};
void i(D);
i({ {1,2}, {"bar"} });  // OK: i(D(A(std​::​initializer_­list<int>{1,2}), C(std​::​string("bar"))))

end example]

В противном случае, если параметр имеет тип агрегата, который может быть инициализирован из списка инициализаторов в соответствии с правилами для aggregate initialization, неявная последовательность преобразования является пользовательской последовательностью преобразования, а вторая стандартная последовательность преобразования является преобразованием идентичности. [Example:

struct A {
  int m1;
  double m2;
};

void f(A);
f( {'a', 'b'} );        // OK: f(A(int,double)) user-defined conversion
f( {1.0} );             // error: narrowing

end example]

В противном случае, если параметр является справочным, см [over.ics.ref]. [ Note: Правила в этом разделе будут применяться для инициализации базового временного объекта для ссылки. ] [ end noteExample:

struct A {
  int m1;
  double m2;
};

void f(const A&);
f( {'a', 'b'} );        // OK: f(A(int,double)) user-defined conversion
f( {1.0} );             // error: narrowing

void g(const double &);
g({1});                 // same conversion as int to double

end example]

В противном случае, если тип параметра не является классом:

  • если в списке инициализаторов есть один элемент, который сам не является списком инициализаторов, неявная последовательность преобразования является той, которая требуется для преобразования элемента в тип параметра; [Example:

    void f(int);
    f( {'a'} );             // OK: same conversion as char to int
    f( {1.0} );             // error: narrowing
    

    end example]

  • если список инициализаторов не имеет элементов, неявная последовательность преобразования является преобразованием идентичности. [Example:

    void f(int);
    f( { } );               // OK: identity conversion
    

    end example]

Во всех случаях, кроме перечисленных выше, преобразование невозможно.

Поскольку нет параметров типа массива, это будет происходить только как ссылочный тип ссылочного параметра.

16.3.3.2 Ranking implicit conversion sequences [over.ics.rank]

В этом подпункте определяется частичный порядок неявных последовательностей преобразования на основе отношений better conversion sequence и better conversion. Если неявная последовательность преобразования S1 определена этими правилами как лучшая последовательность преобразования, чем S2, то это также случай, когда S2 является a, worse conversion sequence чем S1. Если последовательность преобразования S1 не лучше и не хуже, чем последовательность преобразования S2, то говорят, что S1 и S2 indistinguishable conversion sequences.

При сравнении основных форм неявных последовательностей преобразования (как определено в [over.best.ics])

  • a standard conversion sequence - лучшая последовательность преобразования, чем определенная пользователем последовательность преобразования или последовательность преобразования с многоточием, и

  • a user-defined conversion sequence - лучшая последовательность преобразования, чем ellipsis conversion sequence.

Две неявные последовательности преобразования одной и той же формы являются неотличимыми последовательностями преобразования, если не применяется одно из следующих правил:

  • Последовательность инициализации списка L1 является лучшей последовательностью преобразования, чем последовательность инициализации списка, L2 если

    • L1 конвертируется в std​::​initializer_­list<X> для некоторых X и L2 не преобразуется , или, если не то,

    • L1 преобразуется в тип «массив N1 T», L2 преобразуется в тип «массив из N2 T» и N1 меньше, чем N2,

    даже если в противном случае применимо одно из других правил этого параграфа. [Example:

      void f1(int);                                 // #1
      void f1(std::initializer_list<long>);         // #2
      void g1() { f1({42}); }                       // chooses #2
    
      void f2(std::pair<const char*, const char*>); // #3
      void f2(std::initializer_list<std::string>);  // #4
      void g2() { f2({"foo","bar"}); }              // chooses #4
    

    end example]

  • Стандартная последовательность преобразования S1 является лучшей последовательностью преобразования, чем стандартная последовательность преобразования, S2 если

    • S1 является надлежащей подпоследовательностью S2 (сравнение последовательностей преобразования в канонической форме, определенной с помощью [over.ics.scs], исключая любое преобразование Lvalue; последовательность преобразования идентичности считается подпоследовательностью любой последовательности преобразования, отличной от идентичности) или, если это не так,

    • ранг S1 лучше, чем ранг S2, или S1 и S2 имеют такой же ранг и различимы по правилам, изложенным в нижеследующем абзаце, или, если это не так,

    • S1 и S2 являются привязками ссылок ([dcl.init.ref]), и ни одна из них не относится к неявному объектному параметру нестатической функции-члена, объявленной без a ref-qualifier, и S1 связывает ссылку rvalue с rvalue и S2 связывает ссылку lvalue [Example:

      int i;
      int f1();
      int&& f2();
      int g(const int&);
      int g(const int&&);
      int j = g(i);                   // calls g(const int&)
      int k = g(f1());                // calls g(const int&&)
      int l = g(f2());                // calls g(const int&&)
      
      struct A {
        A& operator<<(int);
        void p() &;
        void p() &&;
      };
      A& operator<<(A&&, char);
      A() << 1;                       // calls A​::​operator<<(int)
      A() << 'c';                     // calls operator<<(A&&, char)
      A a;
      a << 1;                         // calls A​::​operator<<(int)
      a << 'c';                       // calls A​::​operator<<(int)
      A().p();                        // calls A​::​p()&&
      a.p();                          // calls A​::​p()&
      

      end example] или, если не это,

    • S1 и S2 являются привязками ссылок ([dcl.init.ref]) и S1 связывает ссылку lvalue с функцией lvalue и S2 связывает ссылку rvalue с функцией lvalue [Example:

      int f(void(&)());               // #1
      int f(void(&&)());              // #2
      void g();
      int i1 = f(g);                  // calls #1
      

      end example] или, если не это,

    • S1 и S2 отличаются только их квалификационным преобразованием и дают аналогичные типы T1 и T2 ([conv.qual]), соответственно, а сигнатура cv-qualification типа T1 является надлежащим подмножеством сигнатуры cv-qualification типа T2 [Example:

      int f(const volatile int *);
      int f(const int *);
      int i;
      int j = f(&i);                  // calls f(const int*)
      

      end example] или, если не это,

    • S1 и S2 являются ссылочными привязками ([dcl.init.ref]), а типы, на которые ссылаются ссылки, относятся к одному и тому же типу, за исключением cv-квалификаторов верхнего уровня, а тип, на который ссылается ссылка, инициализированная с помощью, S2 более квалифицирован cv, чем тип, на который ссылка инициализируется S1 ссылками. [Example:

      int f(const int &);
      int f(int &);
      int g(const int &);
      int g(int);
      
      int i;
      int j = f(i);                   // calls f(int &)
      int k = g(i);                   // ambiguous
      
      struct X {
        void f() const;
        void f();
      };
      void g(const X& a, X b) {
        a.f();                        // calls X​::​f() const
        b.f();                        // calls X​::​f()
      }

      end example]

  • Определяемая пользователем последовательность преобразования U1 является лучшей последовательностью преобразования, чем другая определяемая пользователем последовательность преобразования, U2 если они содержат ту же определенную пользователем функцию преобразования или конструктор или инициализируют один и тот же класс в совокупной инициализации, и в любом случае вторая стандартная последовательность преобразования U1 равна лучше, чем вторая стандартная последовательность преобразования U2. [Example:

    struct A {
      operator short();
    } a;
    int f(int);
    int f(float);
    int i = f(a);                   // calls f(int), because short  int is
                                    // better than short  float.
    

    end example]

Стандартные последовательности конверсии упорядочены по их рангам: точное соответствие - лучшая конверсия, чем продвижение, а это лучшая конверсия, чем конверсия. Две последовательности преобразования с одинаковым рангом неразличимы, если не применяется одно из следующих правил:

  • Преобразование, которое не преобразует указатель, указатель на член или std​::​nullptr_­t на bool , лучше, чем такое преобразование .

  • Преобразование, которое продвигает перечисление, базовый тип которого фиксирован для его базового типа, лучше, чем преобразование, которое продвигает в продвинутый базовый тип, если они разные.

  • Если класс B является производным прямо или косвенно от класса A, преобразование B* в A* лучше, чем преобразование B* в void*, а преобразование A* в void* лучше, чем преобразование B* в void*.

  • Если класс B является производным прямо или косвенно от класса, A а класс C является производным прямо или косвенно от B,

    • преобразование C* в B* лучше, чем преобразование C* в A*, [Example:

      struct A {};
      struct B : public A {};
      struct C : public B {};
      C* pc;
      int f(A*);
      int f(B*);
      int i = f(pc);                  // calls f(B*)
      

      end example]

    • привязка выражения типа C к ссылке на тип B лучше, чем привязка выражения типа C к ссылке на тип A,

    • преобразование A​::​* в B​::​* лучше, чем преобразование A​::​* в C​::​*,

    • преобразование C в B лучше, чем преобразование C в A,

    • преобразование B* в A* лучше, чем преобразование C* в A*,

    • привязка выражения типа B к ссылке на тип A лучше, чем привязка выражения типа C к ссылке на тип A,

    • преобразование B​::​* в C​::​* лучше, чем преобразование A​::​* в C​::​*, и

    • преобразование B в A лучше, чем преобразование C в A.

    [ Note: Сравниваемые последовательности преобразования будут иметь разные типы источников только в контексте сравнения второй стандартной последовательности преобразования инициализации посредством пользовательского преобразования (см. [over.match.best]); во всех других контекстах исходные типы будут такими же, а целевые типы будут разными. ] end note

16.4 Address of overloaded function [over.over]

Использование имени перегруженной функции без аргументов разрешается в определенных контекстах для функции, указателя на функцию или указателя на функцию-член для конкретной функции из набора перегрузки. Имя шаблона функции считается именем набора перегруженных функций в таких контекстах. Функция с типом F выбирается для типа функции FT целевого типа, требуемого в контексте, если F (после возможного применения function pointer conversion) идентична FT. [ Note: То есть класс, членом которого является функция, игнорируется при сопоставлении типа указателя на функцию-член. ] Цель может бытьend note

Перед именем перегруженной функции может стоять & оператор. Имя перегруженной функции не должно использоваться без аргументов в контекстах, отличных от перечисленных. [ Note: Любой лишний набор круглых скобок, окружающий имя перегруженной функции, игнорируется ([expr.prim]). ]end note

Если имя является шаблоном функции, выполняется вывод аргументов шаблона ([temp.deduct.funcaddr]), и если вывод аргументов завершается успешно, результирующий список аргументов шаблона используется для генерации одной специализации шаблона функции, которая добавляется к набору рассматриваемых перегруженных функций. [ Note: Как описано в [temp.arg.explicit], если вывод не удастся и за именем шаблона функции следует явный список аргументов шаблона, template-id затем проверяется, идентифицирует ли он конкретную специализацию шаблона функции. Если это так, то template-id считается lvalue для этой специализации шаблона функции. Тип цели не используется в этом определении. ]end note

Функции, не являющиеся членами, и статические функции-члены соответствуют целям типа указателя функции или ссылки на тип функции. Нестатические функции-члены соответствуют целям указателя на тип функции-члена. Если выбрана нестатическая функция-член, ссылка на имя перегруженной функции должна иметь форму указателя на член, как описано в [expr.unary.op].

Если выбрано более одной функции, любые специализации шаблона функции в наборе удаляются, если набор также содержит функцию, которая не является специализацией шаблона функции, и любая заданная специализация шаблона функции удаляется, F1 если набор содержит вторую специализацию шаблона функции, чья шаблон функции является более специализированным, чем шаблон функции в F1 соответствии с правилами частичного упорядочивания [temp.func.order]. После таких исключений, если таковые имеются, останется только одна выбранная функция.

[Example:

int f(double);
int f(int);
int (*pfd)(double) = &f;        // selects f(double)
int (*pfi)(int) = &f;           // selects f(int)
int (*pfe)(...) = &f;           // error: type mismatch
int (&rfi)(int) = f;            // selects f(int)
int (&rfd)(double) = f;         // selects f(double)
void g() {
  (int (*)(int))&f;             // cast expression as selector
}

Инициализация pfe неправильно сформирована, потому что не был объявленf() тип with int(...), а не из-за какой-либо двусмысленности. Другой пример:

struct X {
  int f(int);
  static int f(long);
};

int (X::*p1)(int)  = &X::f;     // OK
int    (*p2)(int)  = &X::f;     // error: mismatch
int    (*p3)(long) = &X::f;     // OK
int (X::*p4)(long) = &X::f;     // error: mismatch
int (X::*p5)(int)  = &(X::f);   // error: wrong syntax for
                                // pointer to member
int    (*p6)(long) = &(X::f);   // OK

end example]

[ Note: Если обеf() и g()являются перегруженными функциями, для разрешения необходимо учитывать перекрестное произведение возможностей f(&g)или эквивалентное выражение f(g). ]end note

[ Note: Даже если B это общедоступная база D, у нас есть

D* f();
B* (*p1)() = &f;                // error

void g(D*);
void (*p2)(B*) = &g;            // error

end note]

16.5 Overloaded operators [over.oper]

Объявление функции, имеющее одно из следующих operator-function-ids названий, объявляет operator function. Объявление шаблона функции, имеющее одно из следующих operator-function-ids названий, объявляет operator function template. Специализация шаблона операторной функции также является операторной функцией. Операторная функция называется implement оператором, указанным в его operator-function-id.

operator-function-id:
	operator operator
operator: one of
	new	delete	new[]	delete[]
	+	-	*	/	%	^	&	|	~
	!	=	<	>	+=	-=	*=	/=	%=
	^=	&=	|=	<<	>>	>>=	<<=	==	!=
	<=	>=	&&	||	++	--	,	->*	->
	()	[]

[ Note: Последние два оператора - function call и subscripting. Операторы new[], delete[], ()и [] образуются из более чем одного маркеров. ] end note

И унарная, и бинарная формы

+    -    *     &

может быть перегружен.

Следующие операторы не могут быть перегружены:

.    .*   ::    ?:

также нельзя использовать символы предварительной обработки # и ## (пункт [cpp]).

Операторные функции обычно не вызываются напрямую; вместо этого они вызываются для оценки реализуемых ими операторов ([over.unary] - [over.inc]). Однако их можно вызвать явно, используя в operator-function-id качестве имени функции в синтаксисе вызова функции ([expr.call]). [Example:

complex z = a.operator+(b);     // complex z = a+b;
void* p = operator new(sizeof(int)*n);

end example]

Функции распределения и освобождения, operator new, operator new[], operator delete и operator delete​[], описаны полностью [basic.stc.dynamic]. Атрибуты и ограничения, найденные в остальной части этого подпункта, не применяются к ним, если явно не указано в [basic.stc.dynamic].

Операторная функция должна быть либо нестатической функцией-членом, либо функцией, не являющейся членом, которая имеет по крайней мере один параметр, тип которого является классом, ссылкой на класс, перечислением или ссылкой на перечисление. Невозможно изменить приоритет, группировку или количество операндов операторов. Значение операторов =(унарный) &и , (запятая), предопределенных для каждого типа, можно изменить для конкретных классов и типов перечисления, определив операторные функции, которые реализуют эти операторы. Операторные функции наследуются так же, как и другие функции базового класса.

Тождества между некоторыми предопределенными операторами, применяемыми к базовым типам (например, ++a a+=1), не обязательно должны сохраняться для операторных функций. Некоторые предопределенные операторы, например +=, требуют, чтобы операнд был lvalue при применении к базовым типам; это не требуется для операторных функций.

Операторная функция не может иметь default arguments, за исключением случаев, явно указанных ниже. Функции оператора не могут иметь больше или меньше параметров, чем число, необходимое для соответствующего оператора, как описано в оставшейся части этого подпункта.

Операторы не упоминается явно в подразделах [over.ass] через [over.inc] действуют как обычные одинарные и бинарные операторы подчиняясь правилам [over.unary] или [over.binary].

16.5.1 Unary operators [over.unary]

Префиксный унарный оператор должен быть реализован нестатической функцией-членом ([class.mfct]) без параметров или функцией, не являющейся членом, с одним параметром. Таким образом, для любого одноместной операции префикса @, @x может быть интерпретирована как либо x.operator@() или operator@(x). Если были объявлены обе формы операторной функции, правила [over.match.oper] определяют, какая интерпретация используется, если таковая имеется. См. [over.inc] Объяснение постфиксных унарных операторов ++ и --.

Считается, что унарная и двоичная формы одного и того же оператора имеют одно и то же имя. [ Note: Следовательно, унарный оператор может скрыть бинарный оператор из охватывающей области, и наоборот. ]end note

16.5.2 Binary operators [over.binary]

Бинарный оператор должен быть реализован либо нестатической функцией-членом ([class.mfct]) с одним параметром, либо функцией, не являющейся членом, с двумя параметрами. Таким образом, для любого бинарного оператора @, x@y может быть интерпретирована как либо x.operator@(y) или operator@(x,y). Если были объявлены обе формы операторной функции, правила [over.match.oper] определяют, какая интерпретация используется, если таковая имеется.

16.5.3 Assignment [over.ass]

Оператор присваивания должен быть реализован нестатической функцией-членом с ровно одним параметром. Поскольку оператор присваивания копии operator= неявно объявляется для класса, если он не объявлен пользователем ([class.copy]), оператор присваивания базового класса всегда скрывается оператором присваивания копии производного класса.

Любой оператор присваивания, даже операторы присваивания копирования и перемещения, могут быть виртуальными. [ Note: Для производного класса D с базовым классом, B для которого было объявлено виртуальное назначение копирования / перемещения, оператор назначения копирования / перемещения в D не отменяет Bвиртуальный оператор назначения копирования / перемещения. [Example:

struct B {
  virtual int operator= (int);
  virtual B& operator= (const B&);
};
struct D : B {
  virtual int operator= (int);
  virtual D& operator= (const B&);
};

D dobj1;
D dobj2;
B* bptr = &dobj1;
void f() {
  bptr->operator=(99);          // calls D​::​operator=(int)
  *bptr = 99;                   // ditto
  bptr->operator=(dobj2);       // calls D​::​operator=(const B&)
  *bptr = dobj2;                // ditto
  dobj1 = dobj2;                // calls implicitly-declared D​::​operator=(const D&)
}

end example] ]end note

16.5.4 Function call [over.call]

operator() должна быть нестатической функцией-членом с произвольным числом параметров. У него могут быть аргументы по умолчанию. Он реализует синтаксис вызова функции

postfix-expression ( expression-listopt )

где postfix-expression оценивается как объект класса, а возможно пустое значение expression-list соответствует списку параметров operator() функции-члена класса. Таким образом, вызов x(arg1,...) интерпретируется как x.operator()(arg1, ...) объект класса x типа, T если он T​::​operator()(T1, T2, T3) существует, и если оператор выбран как функция наилучшего соответствия механизмом разрешения перегрузки ([over.match.best]).

16.5.5 Subscripting [over.sub]

operator[] должен быть нестатической функцией-членом с одним параметром. Он реализует синтаксис индексации

postfix-expression [ expr-or-braced-init-list ]

Таким образом, индексирующее выражение x[y] интерпретируется как x.operator[](y) объект класса x типа, T если он T​::​operator[](T1) существует и если оператор выбран как функция наилучшего соответствия механизмом разрешения перегрузки ([over.match.best]). [Example:

struct X {
  Z operator[](std::initializer_list<int>);
};
X x;
x[{1,2,3}] = 7;           // OK: meaning x.operator[]({1,2,3})
int a[10];
a[{1,2,3}] = 7;           // error: built-in subscript operator

end example]

16.5.6 Class member access [over.ref]

operator-> должна быть нестатической функцией-членом, не имеющей параметров. Он реализует синтаксис доступа к членам класса, который использует ->.

postfix-expression -> templateopt id-expression
postfix-expression -> pseudo-destructor-name

Выражение x->m интерпретируется как (x.operator->())->m объект класса x типа, T если он T​::​operator->() существует и если оператор выбран как функция наилучшего соответствия механизмом разрешения перегрузки ([over.match]).

16.5.7 Increment and decrement [over.inc]

Вызываемая пользователем функция operator++ реализует префиксный и постфиксный ++ оператор. Если эта функция является нестатической функцией-членом без параметров или функцией-членом с одним параметром, она определяет оператор приращения префикса ++ для объектов этого типа. Если функция является нестатической функцией-членом с одним параметром (который должен иметь тип int) или функцией, не являющейся членом, с двумя параметрами (второй из которых должен иметь тип int), она определяет оператор постфиксного приращения ++ для объектов этого типа. тип. Когда постфиксное приращение вызывается в результате использования ++ оператора, int аргумент будет иметь нулевое значение.134 [Example:

struct X {
  X&   operator++();            // prefix ++a
  X    operator++(int);         // postfix a++
};

struct Y { };
Y&   operator++(Y&);            // prefix ++b
Y    operator++(Y&, int);       // postfix b++

void f(X a, Y b) {
  ++a;                          // a.operator++();
  a++;                          // a.operator++(0);
  ++b;                          // operator++(b);
  b++;                          // operator++(b, 0);

  a.operator++();               // explicit call: like ++a;
  a.operator++(0);              // explicit call: like a++;
  operator++(b);                // explicit call: like ++b;
  operator++(b, 0);             // explicit call: like b++;
}

end example]

Операторы префиксного и постфиксного декремента -- обрабатываются аналогично.

operator++ Явный вызов , как в выражениях вроде a.operator++(2), не имеет особых свойств: аргумент для operator++ is 2.

16.5.8 User-defined literals [over.literal]

literal-operator-id:
	operator string-literal identifier
	operator user-defined-string-literal

В string-literalили user-defined-string-literal в a literal-operator-idне должно быть encoding-prefixи не должно быть никаких символов, кроме неявного завершения '\0'. ud-suffixИз user-defined-string-literalили identifierв literal-operator-idназывается literal suffix identifier. Некоторые буквальные идентификаторы суффиксов зарезервированы для будущей стандартизации; см [usrlit.suffix]. Объявление, в котором literal-operator-idиспользуется такой буквальный идентификатор суффикса, неправильно сформировано, диагностика не требуется.

Объявление, имеющее значение declarator-ida, literal-operator-idдолжно быть объявлением функции или шаблона функции в области пространства имен (это может быть friend function), явным экземпляром или специализацией шаблона функции или файлом using-declaration. Функция, объявленная с a, literal-operator-id- это literal operator. Шаблон функции, объявленный с помощью a, literal-operator-id - это literal operator template.

Объявление буквального оператора должно иметь parameter-declaration-clauseэквивалент одного из следующих:

const char*
unsigned long long int
long double
char
wchar_t
char16_t
char32_t
const char*, std::size_t
const wchar_t*, std::size_t
const char16_t*, std::size_t
const char32_t*, std::size_t

Если у параметра есть default argument, программа имеет неправильный формат.

A raw literal operator - это буквальный оператор с одним параметром типа const char*.

Объявление шаблона литерального оператора должно иметь пустое значение, parameter-declaration-clauseа в нем template-parameter-listдолжен быть единственный шаблон, не являющийся template-parameterшаблоном типа parameter pack с типом элемента char.

Литеральные операторы и шаблоны буквальных операторов не должны иметь привязки к языку C.

[ Note: Литеральные операторы и шаблоны буквальных операторов обычно неявно вызываются через user-defined literals. Однако, за исключением ограничений, описанных выше, они являются обычными функциями области пространства имен и шаблонами функций. В частности, они выглядят как обычные функции и шаблоны функций и следуют тем же правилам разрешения перегрузки. Кроме того, они могут быть объявлены inline или constexprмогут иметь внутреннюю или внешнюю связь, могут вызываться явно, их адреса могут быть взяты и т. Д. ] end note

[Example:

void operator "" _km(long double);                  // OK
string operator "" _i18n(const char*, std::size_t); // OK
template <char...> double operator "" _\u03C0();    // OK: UCN for lowercase pi
float operator ""_e(const char*);                   // OK
float operator ""E(const char*);                    // error: reserved literal suffix ([usrlit.suffix], [lex.ext])
double operator""_Bq(long double);                  // OK: does not use the reserved identifier _­Bq ([lex.name])
double operator"" _Bq(long double);                 // uses the reserved identifier _­Bq ([lex.name])
float operator " " B(const char*);                  // error: non-empty string-literal
string operator "" 5X(const char*, std::size_t);    // error: invalid literal suffix identifier
double operator "" _miles(double);                  // error: invalid parameter-declaration-clause
template <char...> int operator "" _j(const char*); // error: invalid parameter-declaration-clause
extern "C" void operator "" _m(long double);        // error: C language linkage

end example]

16.6 Built-in operators [over.built]

Функции операторов-кандидатов, которые представляют встроенные операторы, определенные в разделе [expr] , указаны в этом подразделе. Эти функции-кандидаты участвуют в процессе разрешения перегрузки оператора, как описано в разделе, [over.match.oper] и не используются ни для каких других целей. [ Note: Поскольку встроенные операторы принимают только операнды с неклассовым типом, а разрешение перегрузки оператора происходит только тогда, когда выражение операнда изначально имеет класс или тип перечисления, разрешение перегрузки оператора может разрешаться во встроенный оператор только тогда, когда операнд имеет класс тип, который имеет определяемое пользователем преобразование в тип, не являющийся классом, подходящий для оператора, или когда операнд имеет тип перечисления, который может быть преобразован в тип, подходящий для оператора. Также обратите внимание, что некоторые из функций-кандидатов, приведенные в этом подпункте, более разрешительны, чем сами встроенные операторы. Как описано в [over.match.oper], после того, как встроенный оператор выбран путем разрешения перегрузки, выражение подчиняется требованиям для встроенного оператора, приведенным в пункте [expr], и, следовательно, любым дополнительным семантическим ограничениям, указанным там. Если есть написанный пользователем кандидат с тем же именем и типами параметров, что и встроенная операторная функция-кандидат, встроенная операторная функция скрыта и не включается в набор функций-кандидатов. ]end note

В этом подпункте этот термин promoted integral type используется для обозначения тех интегральных типов, которые сохраняются integral promotion (включая, например, int и, long но исключая, например char). Точно так же этот термин promoted arithmetic type относится к плавающим типам и расширенным целочисленным типам. [ Note: Во всех случаях, когда требуется расширенный целочисленный тип или расширенный арифметический тип, операнд перечислимого типа будет приемлемым посредством целых расширений. ]end note

В оставшейся части этого раздела vq обозначает либоvolatile квалификатор cv , либо его отсутствие.

Для каждой пары (T, vq), где T - арифметический тип, отличный от bool, существуют кандидатные операторные функции вида

vq T& operator++(vq T&);
T operator++(vq T&, int);

Для каждой пары (T, vq), где T - арифметический тип, отличный от bool, существуют кандидатные операторные функции вида

vq T& operator--(vq T&);
T operator--(vq T&, int);

Для каждой пары (T, vq), где T - тип объекта cv-Qualified или cv-unqualified, существуют кандидатные операторные функции вида

T*vq& operator++(T*vq&);
T*vq& operator--(T*vq&);
T*    operator++(T*vq&, int);
T*    operator--(T*vq&, int);

Для каждого типа объекта cv-qual или cv-unqualified Tсуществуют кандидатные операторные функции в форме

T&    operator*(T*);

Для каждого типа функции T , не имеющего cv-квалификаторов или a ref-qualifier, существуют операторные функции-кандидаты в форме

T&    operator*(T*);

Для каждого типа T существуют кандидатные операторные функции вида

T*    operator+(T*);

Для каждого продвинутого арифметического типа Tсуществуют кандидатные операторные функции вида

T operator+(T);
T operator-(T);

Для каждого продвигаемого интегрального типа Tсуществуют кандидатные операторные функции вида

T operator~(T);

Для каждого пятиэлементного (C1, C2, T, cv1, cv2), где C2 представляет собой тип класса, C1 имеет тот же тип , как C2 и является производным классом C2, и T представляет собой тип объекта или тип функции, существует кандидаты оператор функция вида

cv12 T& operator->*(cv1 C1*, cv2 T C2::*);

где cv12 объединение cv1 и cv2. Тип возврата показан только для демонстрации; см. [expr.mptr.oper] определение типа результата оператора.

Для каждой пары продвинутых арифметических типов L и Rсуществуют кандидатные операторные функции вида

LR      operator*(L, R);
LR      operator/(L, R);
LR      operator+(L, R);
LR      operator-(L, R);
bool    operator<(L, R);
bool    operator>(L, R);
bool    operator<=(L, R);
bool    operator>=(L, R);
bool    operator==(L, R);
bool    operator!=(L, R);

где LR - результат обычных арифметических преобразований между типами L и R.

Для каждого типа объекта cv-qual или cv-unqualified T существуют кандидатные операторные функции в форме

T*      operator+(T*, std::ptrdiff_t);
T&      operator[](T*, std::ptrdiff_t);
T*      operator-(T*, std::ptrdiff_t);
T*      operator+(std::ptrdiff_t, T*);
T&      operator[](std::ptrdiff_t, T*);

Для каждого T, где T - указатель на тип объекта, существуют кандидатные операторные функции вида

std::ptrdiff_t   operator-(T, T);

Для каждого T, где T - тип перечисления или тип указателя, существуют кандидатные операторные функции вида

bool    operator<(T, T);
bool    operator>(T, T);
bool    operator<=(T, T);
bool    operator>=(T, T);
bool    operator==(T, T);
bool    operator!=(T, T);

Для каждого указателя на тип T или тип члена std​::​nullptr_­t существуют функции оператора-кандидата в форме

bool    operator==(T, T);
bool    operator!=(T, T);

Для каждой пары продвинутых интегральных типов L и Rсуществуют кандидатные операторные функции вида

LR      operator%(L, R);
LR      operator&(L, R);
LR      operator^(L, R);
LR      operator|(L, R);
L       operator<<(L, R);
L       operator>>(L, R);

где LR - результат обычных арифметических преобразований между типами L и R.

Для каждой тройки (L, vq, R), где L представляет собой арифметический тип, и R это способствовало арифметического типа существуют кандидаты оператора функций вида

vq L&   operator=(vq L&, R);
vq L&   operator*=(vq L&, R);
vq L&   operator/=(vq L&, R);
vq L&   operator+=(vq L&, R);
vq L&   operator-=(vq L&, R);

Для каждой пары (T, vq), где T - любой тип, существуют кандидатные операторные функции вида

T*vq&   operator=(T*vq&, T*);

Для каждой пары (T, vq), где T - перечисление или указатель на тип члена, существуют кандидатные операторные функции вида

vq T&   operator=(vq T&, T);

Для каждой пары (T, vq), где T - тип объекта cv-Qualified или cv-unqualified, существуют кандидатные операторные функции вида

T*vq&   operator+=(T*vq&, std::ptrdiff_t);
T*vq&   operator-=(T*vq&, std::ptrdiff_t);

Для каждой тройки (L, vq, R), где L представляет собой интегральный тип, и R это способствовало интегрального типа существуют кандидаты оператора функций вида

vq L&   operator%=(vq L&, R);
vq L&   operator<<=(vq L&, R);
vq L&   operator>>=(vq L&, R);
vq L&   operator&=(vq L&, R);
vq L&   operator^=(vq L&, R);
vq L&   operator|=(vq L&, R);

Также существуют кандидатные операторные функции вида

bool    operator!(bool);
bool    operator&&(bool, bool);
bool    operator||(bool, bool);

Для каждой пары продвинутых арифметических типов L и Rсуществуют кандидатные операторные функции вида

LR      operator?:(bool, L, R);

где LR - результат обычных арифметических преобразований между типами L и R. [ Note: Как и все эти описания функций-кандидатов, это объявление служит только для описания встроенного оператора с целью разрешения перегрузки. Оператор «?:» не может быть перегружен. ]end note

Для каждого типа T, где T - указатель, указатель на член или тип перечисления с ограниченной областью действия, существуют кандидатные операторные функции в форме

T       operator?:(bool, T, T);