std :: any Class в C ++

Опубликовано: 30 Декабря, 2021

any - одна из новейших функций C ++ 17, которая предоставляет типобезопасный контейнер для хранения одного значения любого типа. С точки зрения непрофессионала, это контейнер, который позволяет хранить в нем любое значение, не беспокоясь о безопасности типа . Он действует как расширение C ++, имитируя поведение, подобное типу объекта в .NET / Java или типу void * в языке C. Он был разработан на основе boost :: any и доступен в заголовочном файле «любой».

Синтаксис:

любой var = значение / объект; 

где значение - это что-то вроде «17» или «Hello World»

Инициализация любого:

any можно построить тремя разными способами, используя:

  1. Копировать инициализацию

    Синтаксис:

    любое имя_переменной = объект / значение;
    
  2. Параметризованный конструктор / инициализатор скобок.

    Синтаксис:

    любое имя_переменной (объект / значение);
    
  3. Использование оператора присваивания

    Синтаксис:

    любое имя_переменной;
    имя_переменной = объект / значение;
    

    Преобразование значения any_var в исходный тип:

    Необходимо использовать any_cast <type> (any_var) для преобразования значения any_var в исходный тип . Если сохраненное значение имеет тип, отличный от того, к которому выполняется приведение, компилятор сгенерирует исключение bad_any_cast.

    Примечание . Тип во время приведения должен точно совпадать с исходным типом. Во время кастинга не происходит автоматического повышения или понижения в должности. Следовательно, необходимо соблюдать особую осторожность при приведении значения к исходному типу.

    Простой пример (иллюстрирует построение / чтение значений любого)

    #include <any>
    #include <iostream>
    #include <string>
    using namespace std;
    int main()
    {
    try {
    // Integer 42: Using the copy initialisation
    any value = 42;
    cout << " Value: "
    << any_cast< int >(value);
    // Using the assignment operator
    // to store a string
    value = "Hello World" ;
    cout << " Value: "
    << any_cast< const char *>(value);
    // Using the parametrized constructor
    any val(19.0);
    cout << " Value: "
    << any_cast< double >(val);
    any val_brace{ string( "Brace Initialisation" ) };
    cout << " Value: "
    << any_cast<string>(val_brace);
    }
    catch (bad_any_cast& e) {
    cout << " "
    << e.what();
    }
    return 0;
    }

    Выход:

     Значение: 42 
     Значение: Hello World 
     Значение: 19 
     Значение: инициализация скобки
    

    Функции участников:

    1. emplace : изменяет содержащийся объект, создавая новый объект напрямую
    2. reset : уничтожает содержащийся объект (вызывает деструктор объекта)
    3. has_value : проверяет, содержит ли «any_variable» значение внутри себя.
    4. type : возвращает идентификатор типа содержащегося значения

    Давайте посмотрим на методы один за другим подробно:

    • Функция- член emplace: emplace похожа на оператор присваивания и используется для замены содержащегося объекта новым объектом.

      Программа:



      // C++ program to demonstrate
      // emplace() method of any class
      #include <any>
      #include <iostream>
      #include <string>
      int main()
      {
      try {
      any value = 4.2;
      cout << " Value: "
      << any_cast< double >(value);
      value.emplace< int >(44);
      cout << " Value: "
      << any_cast< int >(value);
      }
      catch (bad_any_cast& e) {
      cout << " "
      << e.what();
      }
      return 0;
      }

      Выход:

      Значение: 4,2 
      Значение: 44
      
    • reset: он уничтожает содержащийся объект, вызывая деструктор содержащегося объекта.

      Программа:

      // C++ program to demonstrate
      // reset() method of any class
      #include <any>
      #include <iostream>
      #include <string>
      int main()
      {
      try {
      any var = 4.2;
      cout << " Value: "
      << any_cast< double >(var);
      var.reset();
      if (!var.has_value())
      cout << " No value found in var variable" ;
      }
      catch (bad_any_cast& e) {
      cout << " "
      << e.what();
      }
      return 0;
      }

      Выход:

      Значение: 4,2 
      Значение переменной var не найдено
      
    • has_value: эта функция-член используется для проверки, содержит ли объект значение или нет

      Программа:

      // C++ program to demonstrate
      // has_value() method of any class
      #include <any>
      #include <iostream>
      #include <string>
      int main()
      {
      try {
      any var = 9.5;
      cout << " Value: "
      << any_cast< double >(var);
      if (var.has_value())
      cout << " Value found of type "
      << var.type().name();
      }
      catch (bad_any_cast& e) {
      cout << " "
      << e.what();
      }
      return 0;
      }

      Выход:

       Значение: 9,5 
       Найдено значение типа d
      
    • type: эта функция-член возвращает структуру type_info, которую можно использовать для получения свойств сохраненного объекта.

      Программа:

      // C++ program to demonstrate
      // type() method of any class
      #include <any>
      #include <iostream>
      #include <string>
      int main()
      {
      try {
      any var = 12.0f;
      cout << " Type: "
      << var.type().name();
      var = "Hello World" ;
      cout << " Type: "
      << var.type().name();
      }
      catch (bad_any_cast& e) {
      cout << " "
      << e.what();
      }
      return 0;
      }

      Выход:

      Тип: f 
      Тип: PKc
      

    Использование любых

    Типичное использование включает

    1. В библиотеках, когда тип библиотеки должен содержать или передавать что-либо, не зная набора доступных типов.
    2. Сообщение передается
    3. Реализация библиотек синтаксического анализатора для напр. Парсер JSON
    4. Пользовательский интерфейс: элементы управления могут содержать что угодно
    5. Сущностно-компонентная система.

    Одним из основных преимуществ любого из них является его жизнеспособная замена на void *. void * имеет ограниченные возможности (хранит только типы указателей) и считается небезопасным шаблоном.

    Обработка ошибок:

    Есть два варианта обработки ошибок для любого класса:

    1. Использование исключений: bad_any_cast - это исключение, создаваемое возвращающими значение формами any_cast при несоответствии типов.

      Пример:

      // C++ program to demonstrate
      // using exceptions of any class
      #include <any>
      #include <iostream>
      #include <string>
      int main()
      {
      try {
      any var = 12.0f;
      cout << " Value: "
      << any_cast< double >(var);
      }
      catch (bad_any_cast& e) {
      cout << " "
      << e.what();
      }
      return 0;
      }

      Выход

      Ценить:  
      плохой any_cast
      
    2. Возврат указателя: возврат указателя полезен, когда в компиляторе отключен механизм исключения. Эта конкретная перегрузка any_cast возвращает указатель на содержащийся объект, если приведение было успешным, и возвращает nullptr .

      Пример:

      // C++ program to demonstrate
      // returning pointer of any class
      #include <any>
      #include <iostream>
      #include <string>
      using namespace std;
      int main()
      {
      any var = 12.0f;
      // Special Overload of any_cast
      auto * tval = any_cast< float >(&var);
      if (!tval) {
      // Type-mismatch
      cout << " Bad_any_cast " ;
      }
      else {
      // Value of the object
      cout << " Value: "
      << *tval;
      }
      return 0;
      }

      Выход:

      Значение: 12
      

    Соображения по поводу памяти

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

    Но согласно стандарту реализации должны избегать использования динамически выделяемой памяти для небольшого содержащегося значения. Пример: построенный объект содержит только int. Такая оптимизация малых объектов должна применяться только к типам T, для которых is_nothrow_move_constructible_v истинно.

    Обычно это означает, что компилятор должен использовать оптимизацию малого буфера (SBO), при которой определенный объем памяти должен быть зарезервирован для типа, который будет содержаться.

    Из таблицы выше видно, что зарезервированная память для SBO в некоторых случаях может увеличиваться до 64 байтов! (MSVC-64 бит). Это означает, что для каждого объекта любого объекта потребуется 64 байта зарезервированной памяти, даже если объект небольшой, что приводит к значительным накладным расходам памяти.

    Хотя any - очень мощная функция C ++. Это связано со значительными накладными расходами с точки зрения памяти .

    Хотите узнать о лучших видео и практических задачах, ознакомьтесь с базовым курсом C ++ для базового и продвинутого уровня C ++ и курсом C ++ STL для базового уровня плюс STL. Чтобы завершить подготовку от изучения языка к DS Algo и многому другому, см. Полный курс подготовки к собеседованию .
C++