minmax返回可修改的引用 [英] minmax that returns modifiable references

查看:77
本文介绍了minmax返回可修改的引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有了C ++的所有新功能(我认为C ++ 11就足够了),是什么阻止了 std :: minmax 函数返回一对引用的。

With all the new features of C++ (C++11 suffices I think), what prevents from having a std::minmax function that returns a pair of references.

这样,如果一个提供两个可修改的引用,则可以对其进行修改。

In this way, if one feeds two modifable references, they can be modified. Is this opening a can of worms?

#include<functional>
// maybe all this options can be simplified
template<class T1, class T2> struct common;
template<class T> struct common<T, T>{using type = T;};
template<class T> struct common<T const&, T&>{using type = T const&;};
template<class T> struct common<T&, T const&>{using type = T const&;};
template<class T> struct common<T, T&>{using type = T const&;};
template<class T> struct common<T&, T>{using type = T const&;};
template<class T> struct common<T const&, T>{using type = T const&;};
template<class T> struct common<T, T const&>{using type = T const&;};

template<class T1, class T2, class Compare = std::less<>, class Ret = typename common<T1, T2>::type> 
std::pair<Ret, Ret> minmax(T1&& a, T2&& b, Compare comp = {}){
    return comp(b, a) ? 
        std::pair<Ret, Ret>(std::forward<T2>(b), std::forward<T1>(a))
        : std::pair<Ret, Ret>(std::forward<T1>(a), std::forward<T2>(b));
}

测试:

#include<cassert>
int main(){
    {
    int a = 1;
    int b = 10;
    auto& small = minmax(a, b).first;
    assert(small == 1);
    small += 1;
    assert(a == 2);
    }{
    int const a = 1;
    int b = 10;
    auto& small = minmax(a, b).first;
    assert(small == 1);
//    small += 1; error small is const reference, because a was const
    }{
    int a = 1;
    int const b = 10;
    auto& small = minmax(a, b).first;
    assert(small == 1);
//    small += 1; error small is const reference, because a was const
    }{
    int const a = 1;
    int const b = 10;
    auto& small = minmax(a, b).first;
    assert(small == 1);
//    small += 1; error small is const reference, because a was const
    }{
    int b = 10;
    auto& small = minmax(int(1), b).first;
    assert(small == 1);
//   small += 1; error small is const reference, because first argument was const
    }{
    int a = 1;
    auto& small = minmax(a, int(10)).first;
    assert(small == 1);
//   small += 1; error small is const reference, because second argument was const
    }
    {
    int const a = 1;
    auto& small = minmax(a, int(10)).first;
    assert(small == 1);
//    small += 1; error small is const reference, because both arguments are const
    }
    {
//    auto& small = minmax(int(1), int(10)).first; // error, not clear why
    auto const& small = minmax(int(1), int(10)).first; // ok
//    auto small2 = minmax(int(1), int(10)).first; // also ok
    assert(small == 1);
//    small += 1; error small is const reference, because both arguments are const
    }
}


推荐答案

很久以前,有一种纸质文章是由 Howard Hinnant N2199 。其开头的示例演示了您要解决的确切问题:

There was a paper kind of along these lines a long time ago, by Howard Hinnant: N2199. Its very opening example demonstrates the precise problem you're trying to solve:


该函数不能在左手边使用

The function can not be used on the left hand side of an assignment:

int x = 1;
int y = 2;
std::min(x, y) = 3;  // x == 3 desired, currently compile time error


It继续列举一些经常出现的参考问题,混合类型以及对仅移动类型有用的示例,并继续提出 min 和<$ c的新版本$ c> max 可以解决所有这些问题-它在底部包含一个非常全面的实现(在此处粘贴太长了)。基于此实现 minmax()应该很简单:

It goes on to list as examples the frequently dangling reference problem, mixing types, and being useful with move-only types, and goes on to propose new versions of min and max that address all of these problems - it includes a very thorough implementation at the bottom (which is too long to paste here). Implementing minmax() based on that should be pretty straightforward:

template <class T, class U,
    class R = typename min_max_return<T&&, U&&>::type>
inline
std::pair<R, R>    
minmax(T&& a, U&& b)
{
    if (b < a)
        return {std::forward<U>(b), std::forward<T>(a)};
    return {std::forward<T>(a), std::forward<U>(b)};
}

当时论文被拒绝了。不过,它可能会回来。

The paper was rejected at the time. It's possible it could come back though.

能够取回可变引用很不错,但是能够避免 dangling 引用甚至更好。匿名引用我最近看到的一个例子:

Being able to get back mutable references is nice, but being able to avoid dangling references is even nicer. Anonymously quoting from an example I saw recently:


template<typename T> T sign(T); 

template <typename T> 
inline auto frob(T x, T y) -> decltype(std::max(sign(x - y), T(0))) { 
    return std::max(sign(x - y), T(0)); 
} 

此函数对所有输入均具有不确定的行为(最窄的
合约可能?)。

This function has undefined behaviour for all inputs (the narrowest contract possible?).

请注意,您的普通实现有此问题。这些情况:

Note that your common implementation has this problem. These cases:


template<class T> struct common<T, T&>{using type = T const&;};
template<class T> struct common<T&, T>{using type = T const&;};
template<class T> struct common<T const&, T>{using type = T const&;};
template<class T> struct common<T, T const&>{using type = T const&;};


所有悬挂。如果我有什么意思?

all dangle. What this means if I have:

int i = 4;
auto result = your_minmax(i, 5);

结果这是一个 pair< int const& ;, int const&> ,其中之一是对 i 的引用,而另一个则是悬挂的。为了安全起见,所有这些情况都必须使用type = T; 进行。

result here is a pair<int const&, int const&>, one of which is a reference to i and the other of which dangles. All of these cases have to do using type = T; in order to be safe.

这篇关于minmax返回可修改的引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆