是否有一种很好的方法将std :: minmax(a,b)分配给std :: tie(a,b)?

时间:2019-06-24 15:27:13

标签: c++ algorithm reference stl c++17

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

我认为这是直观的代码。干净易懂。太糟糕了,它与const&的{​​{3}}模板一样无法正常工作。如果因此在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';
  

a:5,b:5

此处的预期输出为a: 5, b: 7


我认为这很重要,因为要实现将函数应用于某些范围的转换函数需要直观的lambda声明。例如:

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::minmax,而在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); }是这里最好的方法?

3 个答案:

答案 0 :(得分:57)

您可以为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); }是这里的最佳方法吗?

由于Compare 1 的要求,我将其定为if(b < a) std::swap(a, b);,但是,是的,我怀疑这样做会更快,并且仍然很清楚您想要做什么完成。


[1]  比较应用于对象的函数调用操作的返回值  满足条件比较的类型,在上下文中转换为bool时,  如果调用的第一个参数出现在  在由这种类型引起的严格的弱有序关系中排名第二,并且  否则为false。

答案 1 :(得分:24)

您可以按照一定的简短程度来强制执行此操作,

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

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

说明:内置的一元加运算符,为了与一元减同级符号对称,会按值返回其操作数 (它也执行通常的算术转换,但这不适用于int个)。这意味着它必须创建一个临时文件,即使该临时文件不过是操作数的副本。但是对于在此示例中使用minmax来说就足够了:这里不再交换引用,因为在右边的引用(const int&参数传递给minmax )不要引用与左侧对象相同的对象(由tuple创建的引用的std::tie内部)。

输出是所需的:

  

a:5,b:7

答案 2 :(得分: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(),因此在C ++ 2a之前的版本中,我不必使用using std::swap; swap(a, b)两步通用性,而后者引入了customization point objects