C ++ std :: mem_fn и std :: bind наоборот - PullRequest
0 голосов
/ 24 октября 2019

Итак, я искал использование абстрактных алгоритмов для повторного использования повторяющихся шаблонов в моем коде. В частности, я хочу определить элемент в массиве узлов, который имеет наибольшую «оценку», определяемую путем оценки сложной функции-члена оценки.

После некоторая помощь Я имеюпридумать (C ++ 17)

template <typename FwdIt, typename Eval, typename Pred = std::less<>>
constexpr FwdIt max_eval_element(FwdIt first, FwdIt last, Eval eval, Pred pred = Pred()) {
    FwdIt found = first;
    if (first != last) {
        auto best = eval(*found);
        while (++first != last) {
            if (auto const thisVal = eval(*first);
                pred(best, thisVal)) {
                found = first;
                best = thisVal;
            }
        }
    }
    return found;
}

Итак, рассмотрим мой класс Node:

class Node {
private:
    double val;
public:
    Node(double val) noexcept : val(val) {}

    [[nodiscard]] auto Score1() const noexcept {
        return std::sqrt(std::log(10.0 / val));
    }

    [[nodiscard]] auto Score2(double other) const noexcept {
        return std::sqrt(std::log(other / val));
    }
};

и мой массив узлов:

std::array<Node, 100000> nodes;

Я могуВызовите

auto const& Node = *std::max_eval_element(std::cbegin(nodes), std::cend(nodes), std::mem_fn(&Node::Score1));

, но теперь я хочу повторить это для Score2, где ввод зависит от некоторой локальной переменной ... конечно, я могу написать некоторую лямбда-функцию ... но у нас есть std::bindдля этого, верно? Я знаю, что вы можете вызывать привязку для функции-члена, например

std::bind(this, std::mem_fn(Node::Score1));

Но я хочу наоборот. Что не работает.

auto const& Node = *std::max_eval_element(std::cbegin(nodes), std::cend(nodes), std::bind(std::mem_fn(&Node::Score2), 1.0));

Я попробовал это наоборот, но это также не работает

auto const& Node = *std::max_eval_element(std::cbegin(nodes), std::cend(nodes), std::mem_fn(std::bind(&Node::Score2), 1.0));

Я знаю почему это не такне работает ... функция-член требует указатель объекта в качестве (скрытого) первого аргумента. Но это означало бы, что мы упускаем что-то вроде std::bind_mem_fn или около того ... У нас было std::bind2nd в прошлом, но оно было удалено в C ++ 17 ...

Опять же: конечно, яможно использовать лямбду, но учитывая, что существуют такие вещи, как std:mem_fn и std::bind, и абстрактные алгоритмы - это хорошо ...

Я что-то упускаю, или это просто отсутствует в стандарте?

Ответы [ 2 ]

2 голосов
/ 24 октября 2019

Звонок std::bind(&Node::Score2) является проблемой. Ему не хватает аргументов для передачи на Score2. Вы хотите:

std::bind(&Node::Score2, std::placeholders::_1, 1.0)

Это не указатель на член, поэтому он не является подходящим аргументом для std::mem_fn

В качестве альтернативы, вы можете использовать лямбду

[](const auto & node) { return node.Score2(1.0); }
0 голосов
/ 24 октября 2019

Я пришел к выводу, что лямбда оптимизирует лучше, чем std :: mem_fn

Возьмите этот код:

#include <iostream>
#include <array>
#include <functional>

class Node {
private:
    double val;
public:
    Node(double val) noexcept : val(val) {}

    [[nodiscard]] auto Score() const noexcept {
        return 10.0 / val;
    }
};

template <typename FwdIt, typename Eval, typename Pred = std::less<>>
constexpr FwdIt max_eval_element(FwdIt first, FwdIt last, Eval eval, Pred pred = Pred()) {
    FwdIt found = first;
    if (first != last) {
        auto best = eval(*found);
        while (++first != last) {
            if (auto const thisVal = eval(*first);
                pred(best, thisVal)) {
                found = first;
                best = thisVal;
            }
        }
    }
    return found;
}

int main()
{
    std::array<Node, 10> nodes{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    auto nodeIt1 = max_eval_element(std::cbegin(nodes), std::cend(nodes), std::mem_fn(&Node::Score));
    std::cout << "dist1 " << std::distance(std::cbegin(nodes), nodeIt1) << std::endl;

    auto nodeIt2 = max_eval_element(std::cbegin(nodes), std::cend(nodes), [](Node const& node) { return node.Score(); });
    std::cout << "dist2 " << std::distance(std::cbegin(nodes), nodeIt2) << std::endl;
}

Если вы посмотрите на скомпилированную сборку (например, )на GodBolt , с -O2), результаты std: mem_fn являются вызовом функции

42 movsd   QWORD PTR [rsp+8], xmm1
43 call    Node::Score() const
44 movsd   xmm1, QWORD PTR [rsp+8]

, а лямбда-указатель

69 movsd   xmm1, QWORD PTR .LC0[rip]
70 divsd   xmm1, QWORD PTR [rax]
...