Вы находитесь на странице: 1из 17

СТАНДАРТ С++

Часть четвёртая: неявные преобразования

К. Владимиров, Intel, 2020


mail-to: konstantin.vladimirov@gmail.com
Общий обзор правил перегрузки
• Выбирается множество перегруженных имён [over.dcl]. При этом не все
функции и прочие сущности с одинаковыми именами засчитываются за
перегруженные [over.load]
• Выбирается множество кандидатов (включая операторы, конструкторы и
т.п.)
• Из множества кандидатов выбираются жизнеспособные (viable) для данной
перегрузки [over.match.viable]
➢ Лучшая из жизнеспособных выбирается на основании цепочек неявных
преобразований для каждого параметра [over.best.ics]
• Если она существует, является единственной и доступна в точке вызова, то
перегрузка разрешена успешно, иначе программа ill-formed [over.match]
2
Вопрос для новичка
• Допустим вызов имеет вид
struct S { S(long){} };
void foo(S) {}
int x = 42;
foo(x); // ???
• Может ли такой вызов быть разрешён в языке?

3
Вопрос для новичка
• Допустим вызов имеет вид
struct S { S(long){} };
void foo(S) {}
int x = 42;
foo(x); // ok, int -> long -> S
• Может ли такой вызов быть разрешён в языке?
• Да и легко. Такой вызов строит цепочку неявных преобразований

4 https://godbolt.org/z/fPceaE
Ловушка для новичка
• Но те же самые два уровня неявных преобразований иногда выключаются
struct T { T(int){} };
struct S { S(T){} };
void foo(S) {}
int x = 42;
foo(x); // fail, int -> T -> S
• Но почему это так и от чего это зависит?

5 https://godbolt.org/z/Pd41E7
Разоблачение чёрной магии
• В стандарте явно указано:
• [over.ics.user] A user-defined conversion sequence consists of an initial standard
conversion sequence followed by a userdefined conversion followed by a second
standard conversion sequence
• Именно поэтому int -> long -> S это ок
• И даже int -> long -> S -> const S& это ок
• А вот int -> T -> S это уже проблемы

6
Последовательности преобразований
• Согласно [over.best.ics], есть три категории well-formed последовательностей:
1. Стандартная последовательность преобразований
2. Пользовательская последовательность преобразований
3. Последовательность включающая троеточия
• Разберём их все по очереди чтобы понять что же это за птицы

7
Стандартные преобразования
• Трансформации объектов (ранг точного совпадения)
int arr[10]; int *p = arr; // [conv.array]
• Коррекции квалификаторов (ранг точного совпадения)
int x; const int *px = &x; // [conv.qual]
• Продвижения (ранг продвижения)
int res = true; // [conv.prom]
• Конверсии (ранг конверсии)
float f = 1; // [conv.fpint]

8
Вопрос для новичка
• Следующий код не компилируется:
char* pc;
const char** ppcc = &pc; // fail
• Нарушены правила конверсии квалификаторов
• Что нужно поправить чтобы сохранить указатель на pc как некий указатель на
некий указатель на неизменяемые данные?

9 https://godbolt.org/z/soM4TY
Ответ: нужно больше const
• Ответ довольно прост
char* pc;
const char* const* ppcc = &pc; // ok
• Основная идея в стандарте это cv-combined type
• [conv.qual] a prvalue of type T1 can be converted to type T2 if the cv-combined
type of T1 and T2 is T2
• Интуитивно: если квалификаторы где-то разошлись, то все дальнейшие
кроме самого крайнего должны быть const
[const char* const* const] x = [char **] y

10
Пользовательские преобразования
• Задаются implicit конструктором либо оператором преобразования
struct A {
operator int(); // 1
operator double(); // 2
};
int i = A{}; // calls (1)
• При этом (1) лучше чем (2) потому что для него нужно меньше стандартных
преобразований и интуитивно более короткая цепочка лучше (но любая
цепочка должна включать не более одного пользовательского)

11 https://godbolt.org/z/hP1c7n
Троеточия
• Формально последовательность троеточий относится только к собcтвенно
троеточиям в аргументах [over.ics.ellipsis]
• На всех компиляторах вот это работает
struct S { S(...) {} };
void foo(S s);
void foo(...);
foo(42); // ok, int -> ellipsis -> S?
• Статус этого автору пока не ясен

12 https://godbolt.org/z/74Mq6a
Итак правда про перегрузку
• Есть ровно три вида преобразований: стандартные, пользовательские и
троеточия. И шаблоны и точное совпадение это разновидности стандартных
• Стандартные ранжируются так
• подпоследовательность лучше последовательности
• последовательность с большим рангом лучше чем с меньшим

• При этом ранг последовательности это наименьший ранг из входящих туда


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

13
Небольшая задачка
• Как вы думаете, исходя из изложенного кто тут выиграет?
struct S { S(float) {} };
struct T { T(...) {} };
void foo(T); // 1
void foo(S); // 2
foo(42); // ?

14
Небольшая задачка
• Как вы думаете, исходя из изложенного кто тут выиграет?
struct S { S(float) {} };
struct T { T(int) {} };
void foo(T); // 1
void foo(S); // 2
foo(42); // 1: int -> T
// 2: int -> float -> S
• Никто, потому что длина стандартной подпоследовательности засчитывается
только в конечной фазе

15 https://godbolt.org/z/bcdM9T
Исключения для инициализации
• В некоторых случаев наличие контекста инициализации серьёзно изменяет
набор кандидатов
struct Y { Y(int) {} };
struct A { operator int() { return 14; } };
int main() {
A a{};
Y y1 = a; // FAIL
Y y2{a}; // OK
}
• Понять интуитивно этот пример из [over.best.ics] довольно сложно

16 https://godbolt.org/z/dhcnMz
Китайский перечень
• Животные делятся на:
• принадлежащих Императору,
• набальзамированных,
• прирученных,
• молочных поросят,
• сирен,
• сказочных,
• бродячих собак,
• включённых в эту классификацию,
• бегающих как сумасшедшие,
• бесчисленных,
• нарисованных тончайшей кистью из верблюжьей шерсти,
• прочих,
• разбивших цветочную вазу,
• похожих издали на мух

17

Вам также может понравиться