Несмотря на то, что для программирования не прям часто нужны какие-то нетривиальные/необычные знания, иногда получается к месту использовать какие-то интересные/неожиданные штуки. О том, что мне доводилось юзать/видеть в последнее время, и расскажу ниже.
Известно, что шаблоны функций/классов должны реализовываться в хедерах, потому что при разделении на объявление/определение компилятор не сможет сматчить, где и что ему инстанцировать. Однако это не значит, что нельзя написать шаблон функции в одном месте (например хедере), а инстанцировать его в .cpp (при соблюдении некоторых условий). Делается это при помощи явного инстанцирования в .cpp и extern template в месте, где не нужно инстанцировать этот шаблон:
// .hpp
template <typename T>
T max(T lhs, T rhs) { return lhs; }extern template int max<int>(int, int); // запретили инстанцирование на месте// какой-то .cpp
#include <.hpp>
template int max<int>(int, int);В данном случае мы спасаемся от инстанцирования функции в каждом месте, где происходит
#include <.hpp>, и компилятор сможет найти уже инстанцированную вверсию в .cpp. Получается, можно экономить [на спичках] на инстанцировании, если вы знаете, какие типы будет принимать ваша функция. Ещё чуть-чуть можно увидеть у Жени. Очень рад, что всё время за изучением SFINAE, было потрачено не зря, т.к. приходилось писать какие-то [тривиальные] проверки на наличие поля у структуры в шаблоннах функций, хотя если бы я увидел конструкции с
std::declval и самим SFINAE впервые, мог бы знатно охренеть.Ещё часто приходится юзать стандартные алгоритмы. Видел даже понятный и полезный кейс для
std::stable_sort (переупорядочить элементы поисковой выдачи от поискового движка по типу, не потеряв при этом относительный порядок, который задал движок при ранжировании).Думаю, каждый согласится, что знание типов переменных очень важно. И понимание кода стало затрудняться с появлением
auto. Конечно, можно использовать аккуратно для недублирования типов или там, где они очевидны, но иногда все рамки адекватности нарушаются, из-за чего можно только страдать. Я использую вот такой небольшой трюк, чтобы узнать тип нужной переменной:template <typename T>
struct Type;
[[maybe_unused]] Type<decltype(var)> tt;Объявляем переменную incomplete шаблонного типа, после чего компилятор сообщит, что не получилось создать такую переменную, и напишет полный тип
var без потери важной информации.Т.к. в коде довольно много всяких
std::variant и std::visit рядом с ними, приходится использовать очень крутую обёртку, использование которой выглядит примерно так:std::visit(Overloaded{
[](const Type1& a) {},
[](const Type2& a) {},
[](const Type3& a) {},
[](const auto& a) {},
}, var);А реализуется она вот так:
template <class... Ts>
struct Overloaded : Ts... {
using Ts::operator()...;
};
template <class... Ts>
Overloaded(Ts...)->Overloaded<Ts...>;Кмк оч красиво и просто.
И самая конфета это математика.
Несмотря на то, что у нас дефолтный микрохайлоад с продуктовой разработкой, какие-то базовые штуки из матстата оказались довольно полезными. Например как аккуратно настроить аллерты, учитывая исторические данные, чтобы они пореже флапали. Тут можно покрутить доверительные интервалы и подобное, чтобы подобрать подходящие значения.
В таком ключе примерно понятно, зачем ботать (или хотя бы запоминать ключевые слова) бесконечно неполезные штуки в университете. Вдруг когда-то пригодится : )