cpp-ru / ideas

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

Добавить математического сахара в (multi)set #405

Open apolukhin opened 3 years ago

apolukhin commented 3 years ago

Перенос предложения: голоса +9, -5 Автор идеи: mrgordonfreman

Как минимум хотелось бы иметь перегрузки операторов +=, \= для выражения объединения и разности множеств.

Определяем множество целых чисел p.

std::set<int> p;

Присваиваем p множество {1, 3, 5}.

p = {1, 3, 5};

Здесь все хорошо, есть перегрузка оператора = со списком инициализации. Оперируем символами, а не контейнером.

Далее хотим добавить новые элементы в множество p, т.е. выполнить объединение.

p += {5, 7, 11};

Но так, к сожалению, не работает. Приходится опускаться на уровень контейнера и вызывать соответствующий метод.

p.insert({5, 7, 11});

Добавление через оператор += близко к описанию алгоритма на псевдокоде. Его можно выразить через уже имеющийся insert

set& operator+=( std::initializer_list<value_type> il ) {
    insert(il);
    return *this;
}

template<class Comp, class Alloc>
set& operator+=( std::set<value_type, Comp, Alloc> const& oth ) {
    insert(oth.begin(), oth.end());
    return *this;
}

Перегрузка для отдельного значения не нужна, т.к. запись через список инициализации {1} наглядно показывает, что работаем с множествами.

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

Аналогичным образом можно определить оператор /= для разности множеств.

set& operator/=( std::initializer_list<value_type> il ) {
    for (auto& x: il)
        erase(x);
    return *this;
}

template<class Comp, class Alloc>
set& operator/=( std::set<value_type, Comp, Alloc> const& oth ) {
    for (auto& x: oth)
        erase(x);
    return *this;
}
apolukhin commented 3 years ago

languagelawyer, 4 февраля 2019, 15:53 О нет, только не member-функции!

Andrey Davydov, 5 февраля 2019, 7:12 Во-первых, это сломает существующий код:

using namespace boost::assign;

std::set<int> s;
s += 1,2,3;

Во-вторых, мотивация "на контестах надо экономить число нажатий клавиш" очень слабая.

mrgordonfreman, 5 февраля 2019, 11:45 Andrey Davydov, вроде бы код не ломается

#include <boost/assign.hpp>
#include <iostream>

using namespace boost::assign;

template<class T, class Comp, class Alloc>
std::set<T, Comp, Alloc>& operator +=(std::set<T, Comp, Alloc>& s, std::initializer_list<T> il)
{
    s.insert(il);
    return s;
}

int main()
{
    std::set<int> s;
    s += 1,2,3;
    s += {3,4,5};
    for (auto&x : s)
        std::cout << x << ' ';
    std::cout << std::endl;
}

Andrey Davydov, 5 февраля 2019, 12:59 mrgordonfreman, Вы правы, не ломает, зря я поленился проверить. Тем не менее Ваше предложение мне все равно не нравится, как-то оно неконсистентно с остальной STL. Почему тогда, к примеру, у vector-а push_back а не operator += ?

Ivan Azoyan, 14 февраля 2019, 11:42 Andrey Davydov, справедливости ради (насчёт консистентности STL) замечу, что в basic_string есть такая перегрузка. http://cpp.sh/8ogdg

#include <iostream>
#include <string>

int main() {
  std::basic_string<int> v = { 1, 2, 3, 4 };
  v += 5;
  v += { 6, 7 };
  for (auto i : v) std::cout << i << " "; // 1 2 3 4 5 6 7
}

Andrey Davydov, 14 февраля 2019, 15:09 Ivan Azoyan, вот же ж. Иметь operator +=(initializer_list) для basic_string, но не иметь его для вектора, это неожиданно.