본문 바로가기
💻 programming/c++

[c++] optional

by 연구원-A 2020. 12. 15.
반응형

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에서 T 객체를 내부에 저장하므로 동적 할당이 사용되지도 않습니다.

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

댓글