C++ lectures at MIPT (in Russian). Lecture 16. Metaprogramming

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

На этой лекции мы закончим разговор о SFINAE, рассмотрев два её прикладных аспекта: static_assert и enable_if, а далее двинемся в область метапрограммирования.

В конце лекции начинается самая жара: я не останавливаюсь на простых метапрограммах, а показываю реалистичные примеры из Boost::MPL и Boost::Fusion, кажется на русском такого вообще не было

Лектор: Константин Владимиров
Дата лекции: 14 апреля 2020 года
Звук: Дмитрий Рябцев

Errata:

* докладчик немного запутался когда рассказывал про трюки с enable_if
template typename = enable_if_t не работает, потому что тут enable_if в параметре по умолчанию. В свою очередь, template enable_if_t<....., int> = 1 работает, потому что тут разрешиться должен сам тип шаблонного параметра. Очень разные вещи

* кроме того разумеется unordered map это O(1+) а не O(lgN)
Рекомендации по теме
Комментарии
Автор

1:17:30 - "unordered_map сделает O(logN)"
Почему логарифм?) Это же хэш-таблица)

dymok
Автор

2:53. Не представляю себе выбора между static_assert(sizeof(int) == 4) и int32_t, это же совершенно семантически разный код. Со static_assert(sizeof(int) == 4) мы просто и грубо ограничиваем нашу программу рядом архитектур, запрещая компиляцию под все остальные. А с int32_t мы получим корректный код на всё ещё большом числе архитектур, даже если int имеет ширину, отличную от 4. В конце концов, C++... кроссплатформенный язык и static_assert в большинстве случаев просто недопустим, он делает не то, что хотел бы разработчик

oficsu
Автор

1:12:31 справедливости ради, стоит отметить, что метапрограммирование в D прекрасно. И C++ есть чему поучиться у D в этом плане.

ufabiz
Автор

1:13:05 обалдеть, это ж Пролог. Никогда не думал, что муть про логическое программирование и пролог будут сколь угодно прикладными знаниями, пусть и в качестве аналогии

slavpetrovich
Автор

Прошу прощения, но разве это 47:00 вообще можно "чинить"? По-моему, в этом случае происходит попытка произвести SFINAE на аргументах по умолчанию для типов как параметров шаблонов, некий аналог перегрузки функций по аргументу по умолчанию, что лишено смысла. Ведь аргументы по умолчанию подставляются лишь в случае отсутствия предоставления пользователем конкретного типа, что подразумевается сигнатурой. Поэтому здесь явно переопределение, как и в случае обычных функций. А когда мы делаем SFINAE на типе нетипизированного параметра, "тип шаблона" будет зависеть от этого.
То есть в остальных случаях при составлении списка кандидатов для разрешения перегрузки, компилятору сперва нужно понять, что вообще за функция (сигнатура) будет, а в этом случае "сигнатура" вполне конкретная. И если же менять порядок действий компилятора, получается при определении аргумента по умолчанию шаблона еще во время выбора кандидатов будет происходить частичное инстанциирование. Надеюсь, доходчиво изложил свою точку зрения.

ddvamp
Автор

А нельзя решить проблему с возвращением войда без SFINAE? Например, использованием Type2Type или if constexpr? Мне кажется это будет покрасивее SFINAE

ВячеславШляга
Автор

спасибо за метод enable_if_t<cond, int> = 0. Подскажите, пожалуйста, как сделать через этот подход реализацию вот такой функции:
template<typename Func, typename ...Args>
[[maybe_unused]]
void
caller(Func&& func, Args&& ...args)
{
if constexpr (std::is_same_v<void, decltype(func(args...))>) {
func(args...);
((std::cout << args << ' '), ...);
std::cout << '\n';
} else {
std::cout << func(args...) << '\n';
}
}
чтобы не был испорчен SFINAE я написал вот так:
template<typename Func, typename ...Args>
[[maybe_unused]]
auto
caller_impl(Func&& func, Args&& ...args) -> std::enable_if_t<std::is_same_v<std::invoke_result_t<Func, Args...>, void>>
{
func(args...);
((std::cout << args << ' '), ...);
std::cout << '\n';
}

template<typename Func, typename ...Args>
[[maybe_unused]]
auto
caller_impl(Func&& func, Args&& ...args) -> std::enable_if_t<!std::is_same_v<std::invoke_result_t<Func, Args...>, void>>
{
std::cout << func(args...) << '\n';
}

template<typename Func, typename ...Args>
[[maybe_unused]]
void
caller(Func&& func, Args&& ...args)
{
caller_impl(func, args...);
}
Проблема в том что пачка параметров должна быть последней в списке параметров и нельзя прибегнуть к трюку R2.

DrUlrih