Открыть Электронные книги
Категории
Открыть Аудиокниги
Категории
Открыть Журналы
Категории
Открыть Документы
Категории
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