cpp-ru / ideas

Идеи по улучшению языка C++ для обсуждения
https://cpp-ru.github.io/proposals
Creative Commons Zero v1.0 Universal
90 stars 0 forks source link

Подмешивание новых методов уже существующим классам #35

Closed Neargye closed 3 years ago

Neargye commented 3 years ago

Перенос предложения: голоса +3, -7 Автор идеи: valera_ee

Думаю, многие согласятся, что подход STL не очень удобен в использовании, когда алгоритмы живут сами по себе, а типы данных, к которым применяются эти алгоритмы, сами по себе. Не всегда очевидно какой алгоритм можно или нельзя использовать с тем или иным контейнером. Но STL уже не изменить, но можно улучшить. На примере класса std::vector покажу, как я сделал его немного удобнее для себя.

Есть шаблонный класс, которы реализует некоторый алгоритм, например, заполнение контейнера некоторым значением:

template <class Container>
class add_fill
{
public:
    template<class Arg>
    Container& fill(Arg &&arg) {
        auto p = static_cast<Container*>(this);
        std::fill(p->begin(), p->end(), arg);
        return (*p);
    }
};

Теперь конструирую свой вектор на основе std:: vector :

template <class T>
class my_vector : public std::vector<T>,
                  public add_fill<vector<T>>
{
public:
    template <class ...Args>
    vector(Args&& ...args) : std::vector<T>(args...) {}
    vector(std::initializer_list<T> init) : std::vector<T>(init) {}
};
Таким образом можно добавить множество алгоритмов и оно работает, но есть минус, я вынужден создавать новый тип через наследование.

Куда удобнее написать что-то вроде:

template<class T>
using my_vector_t = std::vector<T, add_fill, add_copy, add_find, add_sort, add_transform>;

my_vector_t<int> my_vec;
my_vec.resize(N);
my_vec.fill(-200_dB);
my_vec.transform_from(other_vec, [](int val){ return 10*val - 5; });
А для ещё большего удобства делать возвращаемый тип не void, а ссылку на себя, в таком случае можно писать так:

my_vec.resize(N).fill(-200_dB).transform_from(other_vec, [](int val){ 
    return 10*val - 5; 
});

Этот приём позволяет много всего интересного и мой пример лишь малая часть, очень бы хотелось, что бы STL из коробки давал возможность создавать удобные типы на основе уже существующих без наследования.

Neargye commented 3 years ago

develoit 9 декабря 2020, 12:33 что мешает нужные алгоритмы свободными функциями сделать?? запилят же когда-нибудь f(x) -> x.f и будет вам счастье)

valera_ee 9 декабря 2020, 13:19 Корень проблемы кроется в том, что алгоритмы работы с данными отделены от данных, и непонятно подхит алгоритм к контейнеру или нет, нужно ковырять справки или разбирать ошибки компилятора.

Например, класс std::array имеет метод fill(), а std::vector - нет, похоже на бардак в STL. Очень длинные строчки кода где используются алгоритмы, Ranges хорошо упростят код, но не решат проблему в целом.

maxon 9 декабря 2020, 15:15 valera_ee, контейнеры, алгоритмы и итераторы -- это не "корень проблемы", а как раз решение многих трудностей. Способ хранения данных и алгоритмы их обработки -- это разные сущности, с какой стати их приколачивать друг к другу гвоздями, ещё и внутри стандартной либы? Это и в прикладном варианте попахивает.

Сложности с "подходит алгоритм к контейнеру или нет" мне тоже непонятны. Большинство - подходит. Практически все контейнеры имеют ForwardIterator, а большинству алгоритмов этого достаточно. Попытка получить RandomAccessIterator у std::forward_list закономерно закончится ошибкой, но я бы не назвал эту ситуацию "непонятно подходит или нет".

valera_ee 9 декабря 2020, 18:22 maxon, ну вот простой житейский пример, я хочу удалить из контейнера элементы неудовлетворяющие предикату, я делаю 2 действия: 1 - дергаю внешнюю функцию std::remove_if 2 - затем возвращённый итератор передаю на вход самого контейнера

people.erase(std::remove_if(people.begin(), people.end(), is_valid), people.end());

Ну проще же писать: people.remove_if(is_valid);

develoit 10 декабря 2020, 3:46 valera_ee, ну ведь и не сложнее писать: remove_if(people, is_valid);

рассматривай не примитивные методы-члены в контейнерах, как специализации обобщенных алгоритмов (в виде свободных функций), оптимальные для этих структур данных (контейнеров).

Топунов Владимир Андреевич 10 декабря 2020, 10:09 Есть вариант получше Unified Call Syntax

apolukhin commented 3 years ago

Дубликат #30