optional
c++17부터는 std::optional, std::any, std::variant가 추가되었습니다. 이 장에서는 std::optional은 언제 어떻게 그리고 왜 써야 하는지 설명하고 있습니다.
std::optional: How, when, and why | C++ Team Blog
값이 있기도 하고 없기도 해야 할 때
만약 어떤 객체를 반환하는 힘수가 때로는 값을 반환하지 않아야 한다면 이런 함수를 어떻게 구현할 수 있을까요? 전통적인 방법은 값이 비어있다는 플래그를 정의하는 것입니다.
void maybe_take_an_int(int value = -1); // an argument of -1 means "no value"
int maybe_return_an_int(); // a return value of -1 means "no value"
이 방법은 빈 값을 표현할 때 사용한 플래그가 다른 용도로는 절대 사용될 일이 없을 때에는 꽤 합리적인 것처럼 보입니다. 그러나 이 플래그를 정의할 명확한 방법이 없다거나 플래그를 포함한 모든 값들에 대해 함수를 정의하고 싶다면 이 방법을 사용할 수 없게 됩니다. 만약 이런 문제가 있다면, 이 값이 유효한지 아닌지를 가리키는 추가적인 boolean 값이 있어야 합니다.
pair<int, bool> maybe_return_an_int();
아직 값이 있으면 안될 때
만약 아직 초기화 되지 않은 맴버 객체를 가진 클래스를 구현해야 한다면 어떻게 할 수 있을까요 (어떨 때는 객체를 가지기도 하고, 어떨 때는 객체를 가지지 않는 클래스)?
어떠한 이유로 생성자에서 이 멤버 객체를 초기화하지 않고, 요청이 왔을 떄 초기화하거나 나중에 해당 객체를 초기화하는 상황을 생각해봅시다. 그럼 이 멤버 객체는 초기화 되었을 때만 소멸돼야 할 것입니다. 이를 구현하는 끔찍한 방법은 new와 초기화 상태를 기록하는 bool을 이용하는 것입니다.
- is_initialized: 초기화 상태를 기록하는 bool
using T = /* some object type */;
struct S {
bool is_initialized = false;
alignas(T) unsigend char maybe_T[sizeof(T)];
void construct_the_T(int arg) {
assert(!is_nitialized);
new (&maybe_T) T (arg);
is_initialized = true;
}
T& get_the_T() {
assert(is_initialized);
return reinterpret_cast<T&>(maybe_T);
}
~S() {
if(is_initialized) {
get_the_T().~T(); // destroy T
}
}
};
std::optional을 이용하는 방법
c++ 17부터는 optional을 이용해서 이러한 문제들을 해결할 수 있습니다. optional
void maybe_take_an_int(optional<int> potential_value = nullopt);
optional<int> maybe_return_an_int();
std::optional의 멤버 함수
- has_value: 전달한 객체가 값을 가지고 있는지 체크한다
: 값을 가지지 않는 객체에 접근하여 value 함수를 호출하면 bad access 예외가 발생한다 - value: 객체가 가지고 있는 값을 반환한다
- value_or: 객체가 가지고 있는 값이 있으면 반환하고 없으면 할당한 값을 반환한다
#include <string>
#include <functional>
#include <iostream>
#include <optional>
// optional can be used as the return type of a factory that may fail
std::optional<std::string> create(bool b) {
if (b)
return "Godzilla";
return {};
}
// std::nullopt can be used to create any (empty) std::optional
auto create2(bool b) {
return b ? std::optional<std::string>{"Godzilla"} : std::nullopt;
}
// std::reference_wrapper may be used to return a reference
auto create_ref(bool b) {
static std::string value = "Godzilla";
return b ? std::optional<std::reference_wrapper<std::string>>{value}
: std::nullopt;
}
int main()
{
std::cout << "create(false) returned "
<< create(false).value_or("empty") << '\n';
// optional-returning factory functions are usable as conditions of while and if
if (auto str = create2(true)) {
std::cout << "create2(true) returned " << *str << '\n';
}
if (auto str = create_ref(true)) {
// using get() to access the reference_wrapper's value
std::cout << "create_ref(true) returned " << str->get() << '\n';
str->get() = "Mothra";
std::cout << "modifying it changed it to " << str->get() << '\n';
}
}
'💻 programming > c++' 카테고리의 다른 글
[c++] template (0) | 2020.12.15 |
---|---|
[c++] any (0) | 2020.12.15 |
[c++] move (0) | 2020.12.15 |
[c++] perfect forwarding (완벽한 전달) (0) | 2020.12.15 |
[c++] async (0) | 2020.12.15 |
댓글