Как это работает? Пример TDLib с шаблонами на C++

138
23 августа 2019, 11:50

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

namespace detail {
template <class... Fs>
struct overload;
template <class F>
struct overload<F> : public F {
  explicit overload(F f) : F(f) {
  }
};
template <class F, class... Fs>
struct overload<F, Fs...>
    : public overload<F>
    , overload<Fs...> {
  overload(F f, Fs... fs) : overload<F>(f), overload<Fs...>(fs...) {
  }
  using overload<F>::operator();
  using overload<Fs...>::operator();
};
}  // namespace detail
template <class... F>
auto overloaded(F... f) {
  return detail::overload<F...>(f...);
}

А потом вызывается сам обработчик для некоего объекта:

td_api::downcast_call(
    *update, overloaded(
                 [this](td_api::updateAuthorizationState &update_authorization_state) {
                   authorization_state_ = std::move(update_authorization_state.authorization_state_);
                   on_authorization_state_update();
                 },
                 [this](td_api::updateNewChat &update_new_chat) {
                   chat_title_[update_new_chat.chat_->id_] = update_new_chat.chat_->title_;
                 },
                 [this](td_api::updateChatTitle &update_chat_title) {
                   chat_title_[update_chat_title.chat_id_] = update_chat_title.title_;
                 },
                 [this](td_api::updateUser &update_user) {
                   auto user_id = update_user.user_->id_;
                   users_[user_id] = std::move(update_user.user_);
                 },
                 [this](td_api::updateNewMessage &update_new_message) {
                   auto chat_id = update_new_message.message_->chat_id_;
                   auto sender_user_name = get_user_name(update_new_message.message_->sender_user_id_);
                   std::string text;
                   if (update_new_message.message_->content_->get_id() == td_api::messageText::ID) {
                     text = static_cast<td_api::messageText &>(*update_new_message.message_->content_).text_->text_;
                   }
                   std::cerr << "Got message: [chat_id:" << chat_id << "] [from:" << sender_user_name << "] ["
                             << text << "]" << std::endl;
                 },
                 [](auto &update) {}));

сама td_api::downcast_call простая, она выясняет тип объекта, и передает его в func.

bool downcast_call(AuthorizationState &obj, const T &func) {
   switch (obj.get_id()) {
     case authorizationStateWaitTdlibParameters::ID:
       func(static_cast<authorizationStateWaitTdlibParameters &>(obj));
       return true;
     case authorizationStateWaitEncryptionKey::ID:
       func(static_cast<authorizationStateWaitEncryptionKey &>(obj));
       return true;

мне не очень понятно, как работает функция overloaded и как выясняется какую именно лямбду вызывать в td_api::downcast_call. overloaded, видимо, создает в итоге вложенную друг в друга кучу объектов struct overload<F, Fs...>, struct overload<F> и так как мы указали, что используем operator() базового класса, то при вызове funcкомпилятор находит подходящий operator() для вызова. Если не так, то поправьте меня пожалуйста. И вообще, есть ли название у такого подхода? Можно ли где-то найти его описание?

Answer 1

Специализация класса overload со множеством параметров наследует от пачки специализаций класса overload с одним параметром. Каждый из этих базовых классов наследует от анонимного класса, созданного лямбда-выражением и имеет оператор () с сигнатурой как у того лямбда-выражения. Затем все перегруженные операторы () из базовых классов переносятся в наследующий класс посредством директив using overload<F>::operator(); using overload<Fs...>::operator();. Этот шаг принципиально важен, так как компилятор может выбирать из перегруженных функций, относящихся только к одному классу.

Таким образом, в результате получается специализация класса overload c набором перегрузок, соответствующим сигнатурам переданных лямбд.

READ ALSO
Рекурсия и фишки

Рекурсия и фишки

Есть задача о фишках, необходимо ее решить рекурсией, вот условие:

134
Как сложить все элементы массива? C++ [закрыт]

Как сложить все элементы массива? C++ [закрыт]

У меня есть задача: сложить все элементы массива заранее не зная сколько их и потом эту сумму сделать значением переменнойПомогите пожалуйста!

142
Дизайн и реализация класса матрицы

Дизайн и реализация класса матрицы

Заранее извеняюсь, если вопрос вам заставит долго читать, но так как вопросов не очень много, решил позволить себя

98
Как работает оператор (). С++

Как работает оператор (). С++

Например у меня есть функция int foo() {return 0;};И где нибудь в main я буду вызывать foo() , а как оператор () работает в данном случае ?

140