Функторы прозрачных операторов

Visual Studio 2013 Preview поддерживает функцию C++14, которая называется (согласно this page) "Функторы прозрачных операторов". Я не понимаю, что это значит. Ближайшее предложение C++14, которое я нашел, это это, но я не уверен, что это одно и то же: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3421

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


person GravityWell    schedule 19.07.2013    source источник
comment
Если это то же самое, он позволяет вам заменить bool less<int>::operator()( int const& lhs, int const& rhs) const { return lhs<rhs; } на auto less<>::operator()(LHS&& lhs, RHS&& rhs)const->decltype( std::forward<LHS>(lhs)<std::forward<RHS>(rhs) ) { return std::forward<LHS>(lhs)<std::forward<RHS>(rhs); }, идеальную прозрачную переадресацию вызова на <.   -  person Yakk - Adam Nevraumont    schedule 19.07.2013
comment
N3421 — то же самое — на той встрече он был принят на C++ 14 без изменений, и это то, что я реализовал в 2013 Preview. Я думал, что в разделе II «Мотивация и масштаб» ясно объяснялась проблема и решение, и я думал, что пример в разделе VIII «Реализация» демонстрирует его использование. Что вас смущает?   -  person Stephan T. Lavavej    schedule 23.07.2013
comment
На странице «Что нового в Visual C++» не упоминается предложение N3421, поэтому я решил спросить здесь. Я ясно об этом сейчас. Спасибо за добавление.   -  person GravityWell    schedule 23.07.2013


Ответы (1)


Предложение о функторах прозрачных операторов существует как способ иметь обобщенные функторы, расположенные в <functional>. Я лично считаю, что в самом предложении есть очень хороший пример, который поможет проиллюстрировать его необходимость. Однако я пойду дальше и попытаюсь объяснить и это.

Предположим, у вас есть функция, очень простая функция:

template<typename T, typename U>
auto less_than(T&& t, U&& u) -> decltype(std::forward<T>(t) < std::forward<U>(u)) {
    return std::forward<T>(t) < std::forward<U>(u);
}

Однако вы хотите использовать эту обобщенную функцию в заголовке <algorithm>. У вас есть два варианта, чтобы сделать его структурным функтором:

struct MyLessThanFunctor {
    template<typename T, typename U>
    auto operator()(T&& t, U&& u) -> decltype(std::forward<T>(t) < std::forward<U>(u)){
        return std::forward<T>(t) < std::forward<U>(u);
    }
};

Или в С++ 14, чтобы сделать полиморфную лямбду:

[](auto&& t, auto&& u) -> decltype(auto) { 
    return std::forward<decltype(t)>(t) < std::forward<decltype(u)>(u); 
}

Оба они очень многословны при использовании в таком алгоритме:

int main() {
    std::vector<int> v = {112,12,1281271,1919101,29181,412,1 };
    std::sort(std::begin(v), std::end(v), MyLessThanFunctor()); // one
    std::sort(std::begin(v), std::end(v), [](auto&& t, auto&& u) -> decltype(auto) { 
        return std::forward<decltype(t)>(t) < std::forward<decltype(u)>(u); 
    });
}

Это предложение направлено на то, чтобы сделать его более компактным и обобщенным, сделав вместо этого следующее:

std::sort(std::begin(v), std::end(v), std::less<>());

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

Предположим, у вас есть необобщенный функтор:

struct Functor {
    bool operator()(uint32_t a, uint32_t b) {
        return a < b;
    }
};

И вы используете его со своим std::vector<uint32_t>, и он отлично работает, но вы забываете о том, что ваш функтор не является обобщенным, и используете его со своим std::vector<uint64_t>. Вы видите возникшую проблему? Элементы будут усечены перед сравнением, что, вероятно, не то, что хотел пользователь. Обобщенные функторы решают эту проблему за вас до того, как она возникнет.

person Rapptz    schedule 19.07.2013
comment
Вы можете использовать std::less и др. даже без реализации этих прозрачных функторов, но это намного более громоздко. std::sort(std::begin(v), std::end(v), std::less<std::remove_reference<decltype(v[0])>::type>()); - person Praetorian; 19.07.2013
comment
Praetorian, рассмотрите гетерогенную функцию lower_bound() в С++ 11, где диапазон и желаемое значение могут иметь разные типы. Метод less‹T› в C++98 принимает (const T&, const T&), поэтому независимо от того, какой T вы выберете, вы не сможете выполнить гетерогенное сравнение. Это важно даже для простых случаев, таких как std::string и const char *. - person Stephan T. Lavavej; 23.07.2013
comment
Эквивалентная лямбда [](auto&& t, auto&& u) -> decltype(auto) { return std::forward<decltype(t)>(t) < std::forward<decltype(u)>(u); }, к сожалению - person David Stone; 22.11.2015