Прозрачные операторы

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

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

Ответ 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>. Вы видите проблему, которая возникла? Элементы будут усечены перед сравнением, что, вероятно, не то, что пользователь хотел. Обобщенные функции решают эту проблему перед вами, прежде чем они возникнут.