Как реализовать лямбда-функцию для алгоритма сортировки, включающего элементы объекта, косвенность и приведение типов?

Я работаю над некоторым кодом, и у меня есть раздел, в котором я выполняю функцию разовой сортировки. Чтобы реализовать это, я решил, что проще всего перегрузить операторную функцию. Я бы предпочел приблизить реализацию сортировки к фактическому вызову, используя какой-то тип boost::bind, boost::phoenix, lambda или какой-либо другой тип реализации. К сожалению, у меня нет доступа к новым функциям C++11. Ниже приведен пример кода.

// In a header
struct foo
{
   char * a;
   char * c_str() { return a; }
}

// In a header
struct bar
{
    foo * X;          

    bar(foo * _X) : X(_X) {}
    bool operator < (const bar& rhs) const
    {
        return std::string(X->c_str()) < std::string(rhs.X->c_str());
    }
};

struct bars : public std::vector<bar> { ... some stuff  };

// Some other header
bars Bs;


// A cpp file
... other stuff happens that fills the Xs vector with objects

...::Function()
{
    // Current use and it works fine
    std::sort(Bs.begin(), Bs.end())

    // Would like something that accomplishes this:
    // std::sort(Bs.begin(), Bs.end(), 
    //   std::string(lhs.X->c_str()) < std::string(rhs.X->c_str()))

    // A non-working example of what I'm trying to do
    // std::sort(Xs.begin(), Xs.end(), 
    //     std::string((bind(bar::X->c_str(), _1)) <
    //     std::string((bind(bar::X->c_str(), _2)) )
}

Я теряюсь, пытаясь понять, как получить доступ к указателям членов, функции-члену, а затем привести результат к функции boost::bind.

Спасибо за помощь.


person ApockofFork    schedule 18.02.2014    source источник


Ответы (1)


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

  • Усильте Феникс bind и lambda
  • Повышение привязки protect

Однако я научился избегать таких ситуаций. Редактировать На самом деле, смотрите ниже одно такое хитроумное изобретение. Я считаю, что это очень подвержено ошибкам и трудно рассуждать.

То, что вы видите, по сути является нарушением Закона Деметры. Если бы вы «просто» написали код (не в лямбде), он уже обрабатывал бы слишком много задач.

Итак, первое, что я бы сделал, это переосмыслил дизайн класса.

Второе, что я бы сделал, это /извлек/различные обязанности из вашего компаратора. Обратите внимание, что компаратор делает три вещи:

  • получить доступ к c_str() X в левой части
  • получить доступ к c_str() X в правой части
  • сравнить два

Первые два шага являются явными кандидатами на экстракцию. Давайте напишем общий компаратор, который останется первым:

template <typename F>
struct compare_by_impl {
    compare_by_impl(F f = F{}) : _f(std::move(f)) {}

    template <typename T, typename U>
    bool operator()(T const& a, U const& b) const {
        return _f(a) < _f(b);
    }
  private:
    F _f;
};

Как всегда, приятно иметь фабричную функцию, которая выводит тип аксессора (в случае, если вы можете просто использовать там Phoenix, это избавит вас от указания (мистических) имен типов, используемых в шаблонах выражений):

template <typename Accessor>
compare_by_impl<Accessor> comparer_by(Accessor&& f) {
    return compare_by_impl<Accessor>(std::forward<Accessor>(f));
}

Теперь вы уже можете переместить реализацию с помощью вызова сортировки:

void Function()
{
    struct accessX_c_str {
        std::string operator()(bar const& b) const { 
            return b.X->c_str();
        }
    };

    std::sort(Bs.begin(), Bs.end(), comparer_by(accessX_c_str()));
}

Я бы лично оставил его там.

Вот еще несколько хитрых приспособлений:

// to avoid `comparer_by`
std::sort(Bs.begin(), Bs.end(), phx::bind(accessX_c_str(), arg1) < phx::bind(accessX_c_str(), arg2));


// to avoid any helper types (!?!?!? untested!)
std::sort(Bs.begin(), Bs.end(), 
        phx::construct<std::string>(phx::bind(&foo::c_str, phx::lambda [ phx::bind(&bar::X, arg1) ](arg1))) 
      < phx::construct<std::string>(phx::bind(&foo::c_str, phx::lambda [ phx::bind(&bar::X, arg1) ](arg2)))
    );
person sehe    schedule 21.02.2014
comment
Хорошо. Только что вспомнил требование С++ 03. Вот все, адаптированное к c++03 (обратите внимание на комментарии и result_type протокол). А вот исходный материал, поддерживающий C++11, для сравнения (DISCLAIMER< /b> Я до сих пор не утверждаю, что штуковина Phoenix /работает/, она компилируется :)) - person sehe; 21.02.2014
comment
К сожалению, пропущено приведение к std::string: версия c++03 адаптирована и c++11 тоже - person sehe; 21.02.2014
comment
Наконец-то у меня было время вернуться и на самом деле изучить этот код, и я понимаю, что вы пытаетесь сделать, хотя я действительно чувствую, что это ужасно много кода для единственного вида, а не то, что мерзость вызовов boost phoenix велика решение. C++11 значительно упрощает одноразовый код. Спасибо за хорошо сделанные примеры. - person ApockofFork; 18.03.2014