четверг, 17 июля 2025 г.

[prog.c++] А ведь в C++ уже давно можно использовать лямбды в качестве локальных функций для упрощения кода...

...думаю я, когда вижу "шедевры" вроде вот такого:

bool data_holder::write_to(const destination & to, const format_version & ver)
{
   if(!m_updated)
      return true;

   std::unique_ptr<abstract_data_writer> writer{
      (ver == version_x_y || ver == version_y_z)
      ? static_cast<abstract_data_writer *>(new legacy_data_writer{to, ver}) : 
      ( (ver == version_z_q)
        ? static_cast<abstract_data_writer *>(new modern_data_writer{to}) :
          throw unsupported_format_version{ver} )
   };

   writer->write_meta_data(...);
   writer->write_headers(...);
   writer->write_comments(...);
   const bool result = writer->complete();

   m_updated = !result;
   return result;
}

Расшифровывать многоэтажное выражение с вложенными тернарными операторами, как по мне, то еще удовольствие. И главный вопрос, который у меня возникает при взгляде на этот код: а почему было не использовать здесь лямбду в качестве локальной функции?

Например, вот так:

bool data_holder::write_to(const destination & to, const format_version & ver)
{
   if(!m_updated)
      return true;

    auto writer = [&]() -> std::unique_ptr<abstract_data_writer>
    {
      if(ver == version_x_y || ver == version_y_z)
         return std::make_unique<legacy_data_writer>(to, ver);
      else if(ver == version_z_q)
         return std::make_unique<modern_data_writer>(to);
      throw unsupported_format_version{ver};
   }();

   writer->write_meta_data(...);
   writer->write_headers(...);
   writer->write_comments(...);
   const bool result = writer->complete();

   m_updated = !result;
   return result;
}

Здесь и вникнуть в суть происходящего проще, да и расширить со временем новым типом writer-а будет проще.

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

bool data_holder::write_to(const destination & to, const format_version & ver)
{
   if(!m_updated)
      return true;

   const auto do_write = [this](abstract_data_writer & writer) {
      writer.write_meta_data(...);
      writer.write_headers(...);
      writer.write_comments(...);
      const bool result = writer.complete();

      m_updated = !result;
      return result;
   };

   if(ver == version_x_y || ver == version_y_z)
   {
      legacy_data_writer writer{to, ver};
      return do_write(writer);
   }
   else if(ver == version_z_q)
   {
      modern_data_writer writer{to};
      return do_write(writer);
   }

   throw unsupported_format_version{ver};
}

Придерживаюсь мнения, что любой из показанных альтернативных вариантов гораздо проще в восприятии и сопровождении, чем оригинал.

PS. Пожалуйста, не спрашивайте почему в оригинальном коде используется static_cast (а он там и не нужен). У меня нет цензурного ответа.

2 комментария:

Alex комментирует...

а как без static_cast? тернарка должна иметь одинаковые типы в обеих частях

eao197 комментирует...

Да, вы правы.