Есть хороший способ назначить 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';
?