Есть хороший способ назначить std :: minmax (a, b) для std :: tie (a, b)?

std::tie(a, b) = std::minmax(a, b);

Я думаю, что это интуитивно понятный код. Чисто и понятно. Жаль, что он не работает как задумано, как шаблоны std::minmax для const&. Следовательно, если значения поменялись местами внутри std::pair<const&, const&> одно присваивание перезапишет другое значение:

auto[a, b] = std::make_pair(7, 5);

std::tie(a, b) = std::minmax(a, b);

std::cout << "a: " << a << ", b: " << b << '\n';

а: 5, б: 5

Ожидаемый результат здесь: a: 5, b: 7.


Я думаю, что это важно, так как реализация функций преобразования для применения функции к некоторым диапазонам требует таких выражений для интуитивно понятных лямбд. Например:

std::vector<int> v{ 0, 1, 0, 2, 0 };
std::vector<int> u{ 1, 0, 1, 0, 1 };

perform(v.begin(), v.end(), u.begin(), [](auto& a, auto& b){ 
    std::tie(a, b) = std::minmax(a, b);    
}); 

//v would be == {0, 0, 0, 0, 0}
//u would be == {1, 1, 1, 2, 1}

Одним из решений, которое я нашел, было создание std::tuple явном виде без каких-либо ссылочных квалификаторов над std::pair<const&, const&> для обеспечения выполнения копии:

std::tie(a, b) = std::tuple<int, int>(std::minmax(a, b)); 

Но эта <int, int> избыточность кажется довольно ужасной, особенно если раньше были упомянуты auto& a, auto& b.


Есть ли хороший, короткий способ выполнить это задание? Может ли быть так, что это неправильное направление и просто сказать if (a >= b) { std::swap(a, b); } if (a >= b) { std::swap(a, b); } будет лучшим подходом здесь?

Ответы

Ответ 1

Вы можете использовать список инициализаторов для minmax:

std::tie(a, b) = std::minmax({a, b});

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

using namespace std::string_view_literals;

auto [a, b] = std::make_pair("foo"sv, "bar"sv);
std::tie(a, b) = std::minmax({a, b});
std::cout << "a: " << a << ", b: " << b << '\n';

Выход:

a: bar, b: foo

Может ли быть так, что это неправильное направление и просто сказать if (a >= b) { std::swap(a, b); } if (a >= b) { std::swap(a, b); } будет лучшим подходом здесь?

Я бы сделал это, if(b < a) std::swap(a, b); из-за требования сравнения 1 но да, я подозреваю, что это будет быстрее, и все еще очень ясно, чего вы хотите достичь.


[1] Compare [...] Возвращаемое значение операции вызова функции, примененной к объекту типа, удовлетворяющего Compare, при контекстном преобразовании в bool выдает true, если первый аргумент вызова появляется перед вторым в строгом слабом отношение порядка, вызванное этим типом, и ложное в противном случае.

Ответ 2

Вы можете применить это с определенной степенью краткости следующим образом.

std::tie(a, b) = std::minmax(+a, +b);

std::cout << "a: " << a << ", b: " << b << '\n';

Объяснение: встроенный унарный оператор плюс, ради симметрии с его унарным минусом, возвращает свой операнд по значению (он также выполняет обычные арифметические преобразования, но это не относится к int s). Это означает, что он должен создать временный объект, даже если этот временный объект - не что иное, как копия операнда. Но для использования minmax в этом примере этого достаточно: обмен ссылками здесь больше не назначается, поскольку ссылки в правой части (const int& аргументы, передаваемые в minmax) не ссылаются на те же объекты, что и в левой части (внутри tuple ссылок, созданного std::tie).

Вывод по желанию:

а: 5, б: 7

Ответ 3

Иногда шаг назад и поиск другого пути окупаются:

if (b < a)
    std::iter_swap(&a, &b);

Это сжато и в целом более эффективно, конечно, по крайней мере на одном уровне. Может быть, упаковать его в свою собственную функцию:

template <class T>
void reorder(T& a, T& b)
noexcept(noexcept(b < a, void(), std::iter_swap(&a, &b))) {
    if (b < a)
        std::iter_swap(&a, &b);
}

Я использую std::iter_swap() поэтому мне не нужно использовать using std::swap; swap(a, b) using std::swap; swap(a, b) двухэтапный для общности в pre-С++ 2a, который вводит объекты настройки, делающие это устаревшим.

Ответ 4

auto [a, b] = std::make_pair(7, 5);
auto [c, d] = std::minmax(a, b);
std::cout << "c: " << c << ", d: " << d << '\n';

?

(живое демо)