minmax, который возвращает модифицируемые ссылки - PullRequest
0 голосов
/ 13 мая 2018

Со всеми новыми возможностями C ++ (я думаю, что C ++ 11 достаточно), что мешает иметь функцию std::minmax, которая возвращает пару ссылок.

Таким образом, если один передает дваизменяемые ссылки, они могут быть изменены.Это открытие червячной банки?

#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
    }
}

1 Ответ

0 голосов
/ 16 августа 2018

Давным-давно был вид бумаги по этим направлениям: Говард Хиннант : N2199 . Самый первый пример демонстрирует конкретную проблему, которую вы пытаетесь решить:

Функция не может использоваться слева от назначения:

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

Далее он приводит в качестве примеров часто встречающуюся справочную проблему, типы смешивания и полезность для типов только для перемещения, а также предлагает новые версии min и max, которые решают все эти проблемы - он включает в себя очень тщательную реализацию в нижней части (которая слишком длинна, чтобы вставить здесь). Реализация minmax(), основанная на этом, должна быть довольно простой:

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)};
}

Бумага была отклонена в то время. Возможно, это может вернуться, хотя.

Приятно возвращать изменчивые ссылки, но возможность избежать висячих ссылок еще приятнее. Анонимно цитирую пример, который я недавно видел:

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)); 
} 

Эта функция имеет неопределенное поведение для всех входов (самое узкое договор возможен?).

Обратите внимание, что ваша реализация common имеет эту проблему. Эти случаи:

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&;};

все болтаться. Что это значит, если у меня есть:

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

result - это pair<int const&, int const&>, один из которых является ссылкой на i, а другой - болтается. Все эти случаи должны сделать using type = T;, чтобы быть в безопасности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...