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

Sta*_*nny 56 c++ algorithm stl reference c++17

std::tie(a, b) = std::minmax(a, b);
Run Code Online (Sandbox Code Playgroud)

我认为这是直观的代码。干净易懂。太糟糕了,它作为的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';
Run Code Online (Sandbox Code Playgroud)

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}
Run Code Online (Sandbox Code Playgroud)

我发现的一个解决方案是在上构造一个std::tuple没有任何引用限定符的显式结构,std::pair<const&, const&>以强制执行副本:

std::tie(a, b) = std::tuple<int, int>(std::minmax(a, b)); 
Run Code Online (Sandbox Code Playgroud)

但是这种<int, int>冗余似乎很糟糕,尤其是在auto& a, auto& b之前已经说过的时候。


有没有一种不错的,简短的方法来执行此分配?难道这是一个错误的方向,只是说if (a >= b) { std::swap(a, b); }这是最好的方法?

Ted*_*gmo 58

您可以将初始化列表用于minmax

std::tie(a, b) = std::minmax({a, b});
Run Code Online (Sandbox Code Playgroud)

就像使用一元加号时一样,这将导致创建临时对象,但它的好处是它也可用于缺少一元加号运算符的类型。

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';
Run Code Online (Sandbox Code Playgroud)

输出:

a: bar, b: foo
Run Code Online (Sandbox Code Playgroud)

难道这是一个错误的方向,只是说if (a >= b) { std::swap(a, b); }这是最好的方法?

if(b < a) std::swap(a, b);之所以会这样做是因为“ 比较1”的要求,但是,是的,我怀疑这样做会更快,并且仍然很清楚您想要完成什么。


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

  • 这具有很好的好处,它也可以用于更多类型(例如,不能一元运算符+ +`std :: string`)。 (6认同)
  • 您可能需要注意,这会导致创建临时对象,因此可能会很昂贵。 (5认同)
  • @NathanOliver好吧,您需要临时工。真的无法解决吗?问题是由于`initializer_list`的缘故,它确实复制而不是移动...也无法解决:-( (2认同)
  • 我同意,这是最短,最不混乱和最可维护的方法。 (2认同)

lub*_*bgr 25

您可以按照一定的简短程度执行此操作,如下所示。

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

std::cout << "a: " << a << ", b: " << b << '\n';
Run Code Online (Sandbox Code Playgroud)

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

输出是所需的:

a:5,b:7


Ded*_*tor 5

有时,退后一步并找到不同的方法是有回报的:

if (b < a)
    std::iter_swap(&a, &b);
Run Code Online (Sandbox Code Playgroud)

这很简洁,而且通常更有效,当然至少是同等的。也许把它打包成自己的函数:

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);
}
Run Code Online (Sandbox Code Playgroud)

我正在使用,std::iter_swap()所以我不必using std::swap; swap(a, b)在 C++2a 之前的通用性中使用两步,它引入了定制点对象,使其过时。