Top.Mail.Ru
Ответы

Задача по программированию C++

Введение:
В стандартной библиотеке C++ (начиная с C++17) есть такой класс, как std::optional,
который предоставляет удобный способ для обработки ситуаций, когда значение может
отсутствовать. Это обертка, которая содержит либо некоторое значение типа T, либо ничего.
Вот несколько примеров, когда std::optional может быть полезен:
1. Функции с возможным отсутствием результата:
Если функция может не вернуть результат по каким-то причинам (например, ошибка
или логические условия), std::optional может использоваться для возвращения значения,
которое явно указывает на отсутствие результата.
2. Замена для нулевых указателей:
Вместо использования нулевых указателей для необязательных значений объектов,
std::optional предоставляет более безопасный и явный способ обозначения
"неопределенного" состояния.
3. Параметры функций со значением по умолчанию:
std::optional может использоваться для параметров функций, чтобы указать, что
параметр может быть опущен. Это особенно полезно, если тип параметра не имеет
естественного "нулевого" значения.
В этой задаче вам необходимо написать свой упрощенный std::optional.
Задача:
Напишите следующий класс:
template <typename T>
class Optional {
public:
Optional();
Optional(const T& value);
T& Value();
T ValueOr(const T& value);
bool HasValue();
void Set(const T& value);
~Optional();
};
Описание:
Optional() - дефолтный конструктор, он ничего не создает в памяти.
Optional
Введение
Задача
2 / 2
Optional(const T& value) - создает объект типа T и задает ему значение равное value
Value() - получает ссылку на объект, который хранит Optional. Гарантируется, что этот
метод вызывается только для тех Optional, которые на самом деле хранят некоторый
объект типа T.
HasValue() - проверяет, хранит ли Optional объект типа T или не хранит.
ValueOr(const T& value) - работает как Value(), если HasValue() = true, а иначе
возвращает value
Set(const T& value) - если до этого Optional не хранил никакой объект, то выделяет
память под новый объект и помещает туда значение value. Если хранил, то удаляет
старый объект и затем создает новый со значением value.
~Optional() - деструктор для Optional
Пример работы:
Optional<int> opt;
assert(
!opt.HasValue()
);
opt.Set(115);
assert(
opt.HasValue()
);
opt.Value() = 911;
assert(
opt.Value() == 911
);
В качестве T вашему Optional может быть передан тип, у которого нет дефолтного
конструктора, нет operator= (т.е к такому объекту нельзя ничего приравнивать), но несмотря
на это Optional должен по-прежнему корректно работать.
В систему отправляйте только код класса и возможно дополнительный код, если таковой
потребуется.

По дате
По рейтингу
Аватар пользователя
Новичок

Вот описание настоящего класса std::optional
Судя по всему, тут мы наблюдаем попытку косплея широко используемого в языке Rust типа Option , которая позволяет в удобной форме представлять наличие или отсутствие чего-либо. Но в Rust Option - это Enum (перечисление) а не класс

12345
 pub enum Option<T>
{ 
    None, 
    Some(T), 
} 

благодаря чему его реализация в машинном коде может быть абстракцией с нулевым расходом ресурсов. Переменная типа Option может иметь два значения: Some (какие-то_данные) или None - никаких данных нет. Обратите внимание: когда мы помещаем что-то в Option, это что-то туда перемещается. Это очень важный момент! Рассмотрим его подробнее:

123456789101112131415
 fn main()  
{ 
    //  Не инициализированный Option 
    let item : Option::<String>; 
    //  Строка, котрую разрешено изменять 
    let mut s = String::from("world"); 
    //  Метод push добавляет символ в конец строки 
    s.push('!'); 
    //  Инициализиаруем Option строкой 
    item = Some( s ); 
    if let Some( val ) = item 
    { 
        println!("Hello, {}", val); 
    } 
} 

Он нормально компилируется и в итоге мы получим надпись "Hello, World!". А теперь попробуем чуть-чуть изменить программу и что-то сделать со строкой s не ДО а ПОСЛЕ того, как мы засунули ее в Some:

12345678910111213141516
 fn main()  
{ 
    //  Не инициализированный Option 
    let item : Option::<String>; 
    //  Строка, котрую разрешено изменять 
    let mut s = String::from("world"); 
    //  Инициализиаруем Option строкой 
    item = Some( s ); 
    //  Метод push добавляет символ в конец строки 
    s.push('!');
    // if будет выполнено только если в Option что-то есть и это что-то будет доступно как val 
    if let Some( val ) = item 
    { 
        println!("Hello, {}", val); 
    } 
} 

Вот что будет при попытке это скомпилировать:

12345678910111213141516171819
 error[E0382]: borrow of moved value: `s` 
  --> src/main.rs:10:5 
   | 
6  |     let mut s = String::from("world"); 
   |         ----- move occurs because `s` has type `String`, which does not implement the `Copy` trait 
7  |     //  Инициализиаруем Option строкой 
8  |     item = Some( s ); 
   |                  - value moved here 
9  |     //  Метод push добавляет символ... 
10 |     s.push('!'); 
   |     ^^^^^^^^^^^ value borrowed here after move 
   | 
help: consider cloning the value if the performance cost is acceptable 
   | 
8  |     item = Some( s.clone() ); 
   |                   ++++++++ 
 
For more information about this error, try `rustc --explain E0382`. 
error: could not compile `rust_option` (bin "rust_option") due to previous error 

Ошибка произошла, потому что s была перемещена внутрь Some и теперь она ей владеет.

Удаленный ответ Ответ удалён