Задача по программированию 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 (перечисление) а не класс
pub enum Option<T>
{
None,
Some(T),
}
благодаря чему его реализация в машинном коде может быть абстракцией с нулевым расходом ресурсов. Переменная типа Option может иметь два значения: Some (какие-то_данные) или None - никаких данных нет. Обратите внимание: когда мы помещаем что-то в Option, это что-то туда перемещается. Это очень важный момент! Рассмотрим его подробнее:
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:
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);
}
}
Вот что будет при попытке это скомпилировать:
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 и теперь она ей владеет.