вторник, 2 декабря 2025 г.

[prog.c++] Интересно, а какой код понятнее?

В современном C++ одни и те же вещи можно сделать по разному.

Например, у нас есть список типов, для каждого из которых нужно сделать какое-то действие. Что-то вроде for_each-а, но для списка типов.

Можно сделать это вот так:

template<typename... Types>
void for_each_type_via_lambda(int arg) {
    const auto action = [arg]<typename T>() {
        std::cout << typeid(T).name() << " - " << arg << std::endl;
    };
    (action.template operator()<Types>(), ...);
}

Здесь используется шаблон лямбда функции, для использования которого нам приходится явно указывать как его вызывать.

А можно сделать более старым способом, без лямбды, но с помощью вспомогательной функции:

template<typename T>
void do_something(int arg) {
    std::cout << typeid(T).name() << " - " << arg << std::endl;
}

template<typename... Types>
void for_each_type(int arg) {
    (do_something<Types>(arg), ...);
}

Результат будет один и тот же. Но вот понятность двух этих вариантов лично для меня совершенно разная.

Интересно, а какой из вариантов более понятен для вас?

PS. Этот пример на wandbox "для поиграться".

7 комментариев:

Stanislav Mischenko комментирует...

Наверное этот

template
void print_names_of_types(int arg) {
((std::cout << typeid(Types).name() << " - " << arg << std::endl), ...);
}

и да, он работает, я проверил в wandbox

eao197 комментирует...

В реальном коде от каждого типа в Types нужно было не typeid брать ;)
typeid использовался только для примера.

Stanislav Mischenko комментирует...

@eao197 Но это не "магия" typeid. Так тоже работает, например

template
auto get_name_of_type() {
return typeid(T).name();
}

template
void print_names_of_types(int arg) {
((std::cout << get_name_of_type() << " - " << arg << std::endl), ...);
}

eao197 комментирует...

Я так понимаю, что ваш код с get_name_of_type является эквивалентом вот этого: https://wandbox.org/permlink/yDe10p40BNfQSvdn
Ну так это и есть один из вариантов, показанных в посте.

Stanislav Mischenko комментирует...

@еао197 Я просто хотел показать, что typeid здесь нипричём. А вообще, по-моему, на вопрос поста я ответил. Чем меньше "сущностей" в коде, тем понятнее, я считаю ;)

eao197 комментирует...

> Чем меньше "сущностей" в коде, тем понятнее, я считаю ;)

Меньше сущностей получилось только за счет того, что вы в своем однострочнике сделали единственную операцию для типа T. Представьте, что она будет не одна, а для каждого типа нужно сделать 5-10 операций.

Stanislav Mischenko комментирует...

@еао197
Ладно, я не понял, чего вы на самом деле добиваетесь, хотя в рамках поста я ответил верно, я считаю ;)
Вот вам, просто шутки ради, вариант с несколькими операциями

template
auto get_name_of_type() {
return typeid(T).name();
}

template
int type_to_int();

template <>
int type_to_int () {
return 1;
}

template <>
int type_to_int () {
return 2;
}

template <>
int type_to_int () {
return 3;
}

template
void print_names_of_types(int arg) {
((std::cout << get_name_of_type (),
std::cout << ":",
std::cout << (type_to_int () + arg),
std::cout << std::endl),
...);
}