Базовый курс C++ (MIPT, ILab). Lecture 16. Специализация и инстанцирование.

preview_player
Показать описание
Лекции в бакалавриате МФТИ по C++ на русском языке.

Продолжая рассмотрение OpenCL C++ API мы случайно изобретём шаблонную специализацию. И вынуждены будем остановиться на этой технике, рассмотрев процесс инстанцирования и технику частичной специализации.

Лектор: Константин Владимиров
Дата лекции: 7 февраля 2022 года
Съёмка: Владислав Белов.
Звук: Дмитрий Рябцев.

Timeline:
00:00 Снова про OpenCL C++ API
03:30 Идея шаблонной специализации
12:30 Соглашение о namespace detail
21:06 Идея характеристик типов
29:22 Инстанцирование и специализации
38:32 Ленивость инстанцирования
48:25 Частичная специализация
59:57 Unique pointers и частичные специализации
01:04:50 Специализация и LSP
01:07:41 Двухфазное разрешение имён
01:15:31 Устранение неоднозначности
01:21:40 Cliffhanger

Errata:
Рекомендации по теме
Комментарии
Автор

Прекрасная лекция, Константин! Спасибо! Ваши студенты - это самые удачливые люди в России, я считаю :)

VladimirSmith
Автор

Нет ничего лучше утром, чем ваша лекция🙂

ntreymi
Автор

55:28 можно привести такой пример частичной специализации по nontype параметру. Если мы решаем задачу механики, то момент в 3D пространстве будет является тензором 1 ранга (вектор размера dim), а в 2D пространстве это момент вырождается в скаляр. Тогда можно использовать такой шаблон:

template <int dim, typename Number>
struct Moment
{};

template <typename Number>
struct Moment<2, Number>
{
typedef Number type;
static constexpr unsigned int size = 1;
};

template <typename Number>
struct Moment<3, Number>
{
typedef Tensor<1, 3, Number> type;
static constexpr unsigned int size = 3;
};

vladimirivannikov
Автор

Но ведь 57:30 в реальности так нельзя делать. Количество шаблонных аргументов должно совпадать в примари шаблоне и специализации. Опять скажу, что проблема в терминологии "откусывания", которая тут не подходит, на самом деле мы просто объявляем аргументы от которых зависит специализация, то есть не "откусываем T" а "допустим, есть T и U и шаблонные аргументы vector<T>, vector<U>"

niklkelbon
Автор

Здравствуйте. Небольшая опечатка в таймкоде:

> 01:07:41 Двухфазноре разрешение имён — «двухфазноре»

Forritun
Автор

Здраствуйте. Вопрос к блоку 12:35 - 21:03, разве публично наследуя Wrapper, мы не оставляем классу Device пользоваться теми же перегрузками operator()? И на 19:59 не будет ли достаточно перемещающего оператора присваивания?

TheBriks
Автор

16:52 Наверное на этом (46) и на 49 слайдах аргументом шаблона Wrapper должен быть cl_device_id вместо cl_type?

ddvamp
Автор

Здравствуйте, Константин
Как всегда замечательно. Подскажите пожалуйста, по аналогии с запрещением инстанцирования функций для определённых параметров, можно запрещать инстанцирование класса для определённых параметров и/или частичное инстанцирование?

llytxet
Автор

35:32 Константин, а может так случится, что в двух заголовочных файлах размещены перегруженные шаблоны, и в одном из них так же есть явная специализация, но после подключения этих файлов явная специализация меняет соответствие? Если да, то как от такого защищаться?

ddvamp
Автор

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

StrongfulEd-ehcj
Автор

Wrapper для cl_type --- а не проще взять что-нибудь вроде boost:: intrusive_ptr?

victormustya
Автор

18:26 Тут два вопроса. Почему у Device не определен оператор присваивания перемещением? Или, почему присваивание копированием не делает retain?

qrxqfdt
Автор

8:36 Непонятно как вызывается метод release в деструкторе. Или это не тот release, который фигурирует на слайде 42?

qrxqfdt
Автор

54:38 Однако можно добавить requires безумия (правда я даже не знаю, легитимный ли это код)
#include <concepts>
#include <iostream>

template <auto N>
concept EvenIntegral = && (N % 2 == 0);

template <auto N>
concept OddIntegral = && !EvenIntegral<N>;

template <auto N>
concept MultipleOf3Integral = && (N % 3 == 0);

template <auto N>
concept NotMultipleOf3Integral = && !MultipleOf3Integral<N>;

template <auto N>
concept AnyIntegral =
EvenIntegral<N> || OddIntegral<N> ||
MultipleOf3Integral<N> || NotMultipleOf3Integral<N>;

template <typename T, auto N>
requires AnyIntegral<N>
class Array {
public:
Array() { std::cout << "Any" << std::endl; }
};

template <typename T, auto N>
requires EvenIntegral<N> && NotMultipleOf3Integral<N>
class Array<T, N> {
public:
Array() { std::cout << "Even" << std::endl; }
};

template <typename T, auto N>
requires MultipleOf3Integral<N> && OddIntegral<N>
class Array<T, N> {
public:
Array() { std::cout << "Multiple of 3" << std::endl; }
};

template <typename T, auto N>
requires EvenIntegral<N>
class Array<T, N> {
public:
Array() { std::cout << "Even and multiple of 3" << std::endl; }
};

int main()
{
Array<int, 1>{};
Array<short, 2>{};
Array<unsigned, 3>{};
Array<long, 4>{};
Array<long long, 5>{};
Array<char, 6>{};
Array<bool, 7>{};
Array<std::size_t, 8>{};

return 0;
}

ddvamp
Автор

20:04 А чем это вообще мотивировано (если это понятно из предыдущей лекции, прошу прощения, я её пропустил)? Для меня класс Wrapper похож на shared_ptr, так почему изначально созданные объекты Wrapper не обладают счетчиком 1, но вместо этого мы сами выбираем изначально 0 или 1? Получается, что wrapper не инкапсулирует счетчик, и его можно дергать где-то снаружи? Зачем Wrapper тогда вообще? Просто не понимая, это выглядит как-то неправильно

ddvamp
Автор

20:50 они там не могли сделать конструктор от && ?

niklkelbon