Здравствуйте! Спасибо за лекцию. Вопрос про реализацию оператора сравнения строк (1:01). Будет ли правильной реализация не через специализации, а с использованием string_view?
1:12:00 из любопытства набрал пример и удивился: компилятор его съел (arm clang 19) с "warning: use of function template name with no prior declaration in function call with explicit template arguments is a C++20 extension [-Wc++20-extensions]" Пример компилируется всеми clang версии старше 8й, gcc от 5й (включая trunk) выдает вместе с ошибкой note: 'N::f' declared here 3 | template int f(A*); Про оператор меньше понятней меньше всего. Я нашел только gcc 4й версии с таким сообщением: "error: expected primary-expression before 'int" сложно + непонятно :(
Извините, не очень понятно что именно вы пробовали. Ошибка: godbolt.org/z/hTba8EbTT error: expected primary-expression before 'int' Починили: godbolt.org/z/svrzPEcff Всё это на gcc 14.
35:25 -- ну тут я думаю достаточно простой пример все же -- поскольку вызывая extern ты заверяешь компилятор, что где то там есть инстанцирование -- и он воспринимает енто за чистую монету, как если бы инстанцирование было на месте данного extern -- а коль енто так, то специализация следом ведет к ентой самой ошибке.
Если я не ошибаюсь, то запрещаем инстанциирование конкретно в текущей единице трансляции, то есть априори не может быть "где-то там". Поправьте, если не так.
An explicit instantiation declaration (an extern template) skips implicit instantiation step: the code that would otherwise cause an implicit instantiation instead uses the explicit instantiation definition provided elsewhere (resulting in link errors if no such instantiation exists). This can be used to reduce compilation times by explicitly declaring a template instantiation in all but one of the source files using it, and explicitly defining it in the remaining file Skip - это skip, это не forbid IMHO
Обычно в C++ вы ставите либо static либо inline. Я остановлюсь на причинах этого позже. Но избежать определений статических переменных это в общем случае не поможет.
Спасибо за лекцию. Комментарий насчёт первого примера, возведение в степень: Вы сразу же отмели наивный способ возведения в цикле. Но у этого способа есть одно неоспоримое преимущество - алгоритм работает для любого типа, для которого определена операция умножения, при этом никакие traits или инциализирующее значение для acc не нужны.
35:25 Я так и не понял, проблема в том что код не компилируется (что вроде как логично т.к полная специализация вызывает инстанцирование, которое явно запрещено) или в том что текст ошибки нелогичный?
Полная специализация вызывает инстанцирование? Это поворот. Я всегда думал она его заменяет. То есть компилятор начинает процесс инстанцирования только если НЕ видит полной специализации.
@@tilir Странно, но мой предыдущий ответ пропал ( Может из-за ссылки на Godbolt. Но если коротко - один из вариантов использования, это хелпер-структура для получения значения поля у экземпларов класса. Что-то вроде: template struct member_getter { auto operator()(auto &&attr) const { return (attr.*Fn)({}); }; }; Mangled-ссылка на Compiler Explorer: godbolt_org/z/4x4sohMP6
А можете пожалуйста дать подсказку как найти пункт в стандарте который отражает инстанцирование шаблона при extern декларации, после чего нельзя делать специализацию?
Конечно могу. 13.9 и дальше в окрестностях 13.9.1.5.3 С++20 "both an explicit instantiation and a declaration of an explicit specialization shall not appear in a program unless the explicit instantiation follows a declaration of the explicit specialization"
@@tilir Ох, верно ли я понял, что конструкция из 13.9.3.2 explicit-instantiation: extern(opt) template declaration вне зависимости от наличия extern является explicit-instantiation и поэтому мы не можем специализировать после неё?
extern специализация, это утверждение о том, что где-то специализация уже проведена. Компилятор не в праве этому не верить. При необходимости он отправит символ в таблицу импорта, а дальше пусть линкер разбирается. То есть, компилятор, вполне уверен, что специализация уже есть и не допускает повторной. Говорит он , достаточно внятно. Или я не прав?
37:15 "extern template означает не инстанцировать вообще ничего". Возникает резонный вопрос. Если extern template не инстанцирует ничего, то как остальные единицы трансляции найдут инстанцированное тело функции при линковке? Ведь явное инстанцирование, а значит и само скомпилированное тело функции, лежит в отдельном .cc файле, и при линковке это тело надо как-то найти, а значит остальным единицам трансляции на этапе компиляции должен быть виден прототип этой функции. А шаблоны - это механизм самого компилятора, и к линковке отношения не имеет. Из такой логики, как и из самого синтаксиса, extern template больше выглядит как инстанцирование прототипа функции, а не отсутствие инстанцирования в принципе. Или я где-то не прав?
Прототипы не инстанцируются. И если честно я просто не понял что вас смущает. Да, инстанцировать в данной единице трансляции отказываемся. Как раз и значит, что найдём в другой. Программа по стандарту состоит из нескольких tu, так что в какой-то степени стандарт про линковку знает.
Я бы и на вычитание не всегда надеялся. Сложение и умножение это очень частые вещи над чем угодно, включая, например, строки. Деление это операция немного странная.
добрый день, а можно как нибудь сделать видео про варианты замены дефайнов для генерации разного кода? Ну например, в зависимости от ОС, или включения определенных компонентов программы. Просто получаетя, что с модулями енто невозможно теперь. Из предполагаемых мной вариантов можно применять меташаблонные функции (классы, структуры). Но возможно есть уже варианты более выгодных способов?
У меня будет лекция про модули. Если вы видите конкретные причины по которым модули мешают условной компиляции, напишите их, я попробую их тоже разобрать.
про гетерогенный максимум: 1) если он consteval, то consteval auto max(T lhs, U rhs) { if constexpr(lhs < rhs) return rhs; else return lhs; } 2) вернуть std::variant 3) вернуть std::common_type
(1) Это не будет работать. Внутри consteval функции lhs < rhs это всё равно не константное выражение. Но даже если поправить грубую ошибку, это всё равно не будет работать по очевидным причинам: godbolt.org/z/KqdjnG3cb (2) Это шутка была? (3) Попробуйте.
Уровень знания C++ - джун/миддл/сеньер/гуру/сидишь вечером один, читаешь стандарт и смеёшься
Есть книжка от Страуспа, Дизайн и Эволюция С++. В ней он сидит с чашкой кофе, вспоминает, как писали стандарт, ржет и пишет книгу.
Утро начинается не с кофе...
Спасибо за все ваши видео с лекциями! Предоставляете редкую возможность на самообразование. Мегамегамощь! Спасибо!
Смотрю N-ое видео от Константина по шаблонам. "Ну теперь-то я точно всё пойму." Где-то на тайминге в 1 час: "Так. Что? Перематываем в начало..."
Отличная лекция, очень понятно объясняете! База, которую надо знать. Спасибо!
Большое спасибо за лекции
Так работает: template auto foo(){return n;}
Слушаю лекции перед сном, чтобы от такого большого потока информации, которую нужно обработать, мозг перегрелся и вошёл в аварийную гибернацию. =)
Очень полезный материал, спасибо большое за то что делитесь с нами.
Здравствуйте! Спасибо за лекцию. Вопрос про реализацию оператора сравнения строк (1:01). Будет ли правильной реализация не через специализации, а с использованием string_view?
1:12:00 из любопытства набрал пример и удивился: компилятор его съел (arm clang 19) с "warning: use of function template name with no prior declaration in function call with explicit template arguments is a C++20 extension [-Wc++20-extensions]"
Пример компилируется всеми clang версии старше 8й, gcc от 5й (включая trunk) выдает вместе с ошибкой
note: 'N::f' declared here
3 | template int f(A*);
Про оператор меньше понятней меньше всего. Я нашел только gcc 4й версии с таким сообщением: "error: expected primary-expression before 'int"
сложно + непонятно :(
Извините, не очень понятно что именно вы пробовали.
Ошибка: godbolt.org/z/hTba8EbTT
error: expected primary-expression before 'int'
Починили: godbolt.org/z/svrzPEcff
Всё это на gcc 14.
1:12:31 На, возможно, нечто похожее есть мозговыносная лекция 03. Type loopholes in C++, Убербаг уровня стандарта - Антон Квятковский
Можно ли как то получить Traits из переменной, например для deducing this - self как тип не прокатывает. decltype каждый раз дописывать не хочется))
35:25 -- ну тут я думаю достаточно простой пример все же -- поскольку вызывая extern ты заверяешь компилятор, что где то там есть инстанцирование -- и он воспринимает енто за чистую монету, как если бы инстанцирование было на месте данного extern -- а коль енто так, то специализация следом ведет к ентой самой ошибке.
Если я не ошибаюсь, то запрещаем инстанциирование конкретно в текущей единице трансляции, то есть априори не может быть "где-то там".
Поправьте, если не так.
An explicit instantiation declaration (an extern template) skips implicit instantiation step: the code that would otherwise cause an implicit instantiation instead uses the explicit instantiation definition provided elsewhere (resulting in link errors if no such instantiation exists). This can be used to reduce compilation times by explicitly declaring a template instantiation in all but one of the source files using it, and explicitly defining it in the remaining file
Skip - это skip, это не forbid IMHO
Согласен. Жаль, сначала написал коммент, а потом ваш прочёл.
1:03:48 я везде свой код переписал на static inline. В некоторых случаях помогло удалить ненужные dummy.cpp с объявлениями статических полей.
Обычно в C++ вы ставите либо static либо inline. Я остановлюсь на причинах этого позже. Но избежать определений статических переменных это в общем случае не поможет.
1:11:04 Константин, эту проблему решили в С++20 (P0846R0: ADL and Function Templates that are not Visible)
Да, действительно. Жаль, минус один красивый пример.
Спасибо за лекцию.
Комментарий насчёт первого примера, возведение в степень:
Вы сразу же отмели наивный способ возведения в цикле. Но у этого способа есть одно неоспоримое преимущество - алгоритм работает для любого типа, для которого определена операция умножения, при этом никакие traits или инциализирующее значение для acc не нужны.
Почему не нужны? А с чего вы начнете аккумулировать значения в линейном алгоритме? Всё то же, только медленней.
35:25 Я так и не понял, проблема в том что код не компилируется (что вроде как логично т.к полная специализация вызывает инстанцирование, которое явно запрещено) или в том что текст ошибки нелогичный?
Полная специализация вызывает инстанцирование? Это поворот. Я всегда думал она его заменяет. То есть компилятор начинает процесс инстанцирования только если НЕ видит полной специализации.
Интересно, а когда вы в конце предлагали попробовать самостоятельно написать гетерогенный максимум, то какой стандарт С++ имели ввиду?
Пишите на каком сможете а далее идите вниз. Типа если получилось на C++11, попробуйте на C++98.
Константин, спасибо за материал!
Кажется, разделе про NTTP вы не упомянули указатели на члены класса, а это тоже интересное использование.
Я по ряду причин отложил эту тему на будущее, но, раз уж вы её упомянули, может вы знаете полезные применения?
@@tilir Странно, но мой предыдущий ответ пропал ( Может из-за ссылки на Godbolt.
Но если коротко - один из вариантов использования, это хелпер-структура для получения значения поля у экземпларов класса.
Что-то вроде:
template
struct member_getter
{
auto operator()(auto &&attr) const { return (attr.*Fn)({}); };
};
Mangled-ссылка на Compiler Explorer: godbolt_org/z/4x4sohMP6
А можете пожалуйста дать подсказку как найти пункт в стандарте который отражает инстанцирование шаблона при extern декларации, после чего нельзя делать специализацию?
Конечно могу. 13.9 и дальше в окрестностях 13.9.1.5.3 С++20
"both an explicit instantiation and a declaration of an explicit specialization shall not appear in a program unless the explicit instantiation follows a declaration of the explicit specialization"
@@tilir Ох, верно ли я понял, что конструкция из 13.9.3.2
explicit-instantiation:
extern(opt) template declaration
вне зависимости от наличия extern является explicit-instantiation и поэтому мы не можем специализировать после неё?
extern специализация, это утверждение о том, что где-то специализация уже проведена. Компилятор не в праве этому не верить. При необходимости он отправит символ в таблицу импорта, а дальше пусть линкер разбирается. То есть, компилятор, вполне уверен, что специализация уже есть и не допускает повторной. Говорит он , достаточно внятно. Или я не прав?
37:15 "extern template означает не инстанцировать вообще ничего".
Возникает резонный вопрос. Если extern template не инстанцирует ничего, то как остальные единицы трансляции найдут инстанцированное тело функции при линковке? Ведь явное инстанцирование, а значит и само скомпилированное тело функции, лежит в отдельном .cc файле, и при линковке это тело надо как-то найти, а значит остальным единицам трансляции на этапе компиляции должен быть виден прототип этой функции. А шаблоны - это механизм самого компилятора, и к линковке отношения не имеет. Из такой логики, как и из самого синтаксиса, extern template больше выглядит как инстанцирование прототипа функции, а не отсутствие инстанцирования в принципе. Или я где-то не прав?
Прототипы не инстанцируются. И если честно я просто не понял что вас смущает. Да, инстанцировать в данной единице трансляции отказываемся. Как раз и значит, что найдём в другой. Программа по стандарту состоит из нескольких tu, так что в какой-то степени стандарт про линковку знает.
@@tilir Просто непонятно, на что опирается линкер при поиске функции, если прототипа функции нет
acc=1;
Можно заменить на acc = x/x;
раз есть оператор*, можно надеяться и на обратный ему оператор/
Я бы и на вычитание не всегда надеялся. Сложение и умножение это очень частые вещи над чем угодно, включая, например, строки. Деление это операция немного странная.
добрый день, а можно как нибудь сделать видео про варианты замены дефайнов для генерации разного кода?
Ну например, в зависимости от ОС, или включения определенных компонентов программы.
Просто получаетя, что с модулями енто невозможно теперь.
Из предполагаемых мной вариантов можно применять меташаблонные функции (классы, структуры). Но возможно есть уже варианты более выгодных способов?
У меня будет лекция про модули. Если вы видите конкретные причины по которым модули мешают условной компиляции, напишите их, я попробую их тоже разобрать.
А почему в начальном примере «просто в цикле - плохая идея»?
Потому что простой цикл теряет информацию о том что вы хотите сделать. Загуглите: no raw loops.
Каждый раз во время викторины всё больше ужасаешься языку и всё больше хочется досмотреть до конца.
на 44:41 в аудитории чел просто камень:
-- почему кстати ссылка на массив , а не указатель?
-- ну , по правилам там скобочки ...
20 слайд.
"Как вы думаете, как играет запет инстанцирования со специализацией?"
запет -> запрет
29 слайд.
int b = foo(a);
Но foo возвращает void
про гетерогенный максимум:
1) если он consteval, то consteval auto max(T lhs, U rhs) { if constexpr(lhs < rhs) return rhs; else return lhs; }
2) вернуть std::variant
3) вернуть std::common_type
(1) Это не будет работать. Внутри consteval функции lhs < rhs это всё равно не константное выражение.
Но даже если поправить грубую ошибку, это всё равно не будет работать по очевидным причинам: godbolt.org/z/KqdjnG3cb
(2) Это шутка была?
(3) Попробуйте.