Подтвердить что ты не робот

Использование max <int> в качестве предикатных разрывов в С++ 11

В С++ 03 следующий код работает нормально:

int main()
{
    std::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);

    std::vector<int> v2;
    v2.push_back(2);
    v2.push_back(3);
    v2.push_back(4);

    std::transform(v.begin(), v.end(), v2.begin(), v2.begin(), std::max<int>);
    return 0;
}

В С++ 11 это не работает, потому что добавлена ​​перегрузка для std::max, которая содержит initializer_list. Поэтому вам нужно использовать очень уродливый бросок, чтобы выбрать правильную перегрузку:

static_cast<const int& (*)(const int&, const int&)>(std::max)

У меня есть несколько вопросов.

  • Почему стандартный комитет решил сделать это, зная, что это (возможно) нарушит существующий код и заставит пользователя создать уродливый листинг?
  • Являются ли будущие стандарты С++ попытками облегчить эту проблему?
  • Что такое обходной путь?
4b9b3361

Ответ 1

Если вы делаете это достаточно часто, вы можете написать прозрачную оболочку-функтор:

struct my_max {
    template<class T>
    const T& operator()(const T& a, const T& b) const{
        return std::max(a, b);
    }
};

Тогда вы можете просто сделать

std::transform(v.begin(), v.end(), v2.begin(), v2.begin(), my_max());

всякий раз, когда вам это нужно, вместо того, чтобы писать лямбду или литой каждый раз. Это в основном та же идея, что и прозрачные операторные функторы - пусть аргументы шаблона выводятся на фактическом сайте вызова, а не явно указаны при создании функтора.

Если вы хотите сделать этот любитель, вы можете даже operator() взять гетерогенные типы и добавить отличную переадресацию и использовать возвращающие возвращаемые типы:

struct my_max {
    template<class T, class U>
    constexpr auto operator()( T&& t, U&& u ) const
      -> decltype(t < u ? std::forward<U>(u) : std::forward<T>(t)){
        return t < u ? std::forward<U>(u) : std::forward<T>(t);
    }
};

В С++ 14 это упрощено до

struct my_max {
    template<class T, class U>
    constexpr decltype(auto) operator()( T&& t, U&& u ) const{
        return t < u ? std::forward<U>(u) : std::forward<T>(t);
    }
};

Ответ 2

Хотя я принял ответ TC, который содержит исчерпывающую разбивку, как указано в комментарии, я хочу подражать прозрачному функтору-компаратору для шаблонов классов, таких как std::less. Этот ответ предоставляется для критики другими, если у вас что-то не так с синтаксисом.

template <typename T = void>
struct my_max;

template <>
struct my_max<void> {
    template<class T, class U>
    constexpr decltype(auto) operator()( T&& t, U&& u ) const {
        return t < u ? std::forward<U>(u) : std::forward<T>(t);
    }
};

Ответ 3

Что такое обходной путь?

Лямбда, вероятно, наиболее читаема и полезна для предикатов и компараторов:

std::transform(v.begin(), v.end(), v2.begin(), v2.begin(),
               [] (int a, int b) {return std::max(a,b);} );

Возможно, вы захотите проверить функтор T.C.s, если вам это нужно чаще. Или, с С++ 14:

auto max = [] (auto&& a, auto&& b) -> decltype(auto)
  {return a > b? std::forward<decltype(a)>(a) : std::forward<decltype(b)>(b);};

Почему стандартный комитет решил сделать это, зная, что это будет (возможно) нарушить существующий код и заставить пользователя создавать уродливые отливать?

Единственное объяснение заключается в том, что они обнаружили, что новая перегрузка приносит достаточную радость, чтобы компенсировать нарушение существующего кода и необходимость обходных решений в будущем. Вы можете просто использовать std::max_element вместо этой новой перегрузки, поэтому вы торгуете синтаксическим сахаром для передачи std::max -специализации в качестве предикатов для синтаксического сахара для нахождения максимального элемента внутри пары переменных без явного создания массива для него.

В принципе

std::transform( ..., std::max<int> );
// <=>
std::transform( ..., [] (int a, int b) {return std::max(a,b);} );

против

int arr[] {a,b,c,d}; // You don't have an array with a,b,c,d included consecutively yet
int maximum = *std::max_element( std::begin(arr), std::end(arr) ); // ensure arr non-empty!
// <=>
auto maximum = std::max({a, b, c, d});

Может, это компенсирует? С другой стороны, вам едва ли понадобится последняя.

Являются ли будущие стандарты С++ попытками смягчить этот проблема?

Я так не думаю. По-видимому, стандартный комитет действительно не любит удалять недавно введенные функции. Я тоже не вижу такой проблемы; Лямбда выполняет эту работу.