C++/STL

[C++17] Optional

sseram 2023. 5. 28. 15:06
반응형

https://en.cppreference.com/w/cpp/utility/optional

 

std::optional - cppreference.com

template< class T > class optional; (since C++17) The class template std::optional manages an optional contained value, i.e. a value that may or may not be present. A common use case for optional is the return value of a function that may fail. As opposed

en.cppreference.com

 

optional.

값이 존재할 수도 있고, 존재하지 않을 수도 있는 값을 표현하기 위해 사용되는 standard library. 주로 실패할 수도 있는 함수의 반환 값을 줄 때, 읽기 쉽고 좀 더 명확하게 코드에 나타낼 수 있어서 좋은 방법.. 이라고 공식 문서에서 설명되어있다.

대단한 기능이 생겼다! 라는 느낌 보다는, 좀 더 안전하게 코드 작성이 가능해졌다. 가 맞는 표현일 듯 하다.

 


#include <iostream>

using namespace std;

int* getValue(bool cond) {
    if (cond) {
        static int value = 42;
        return &value;
    } else {
        cout << "error! cannot get value" << endl;
        return nullptr;
    }
}

int main() {
    int* optionalValue = getValue(true);

    if (optionalValue) {
        cout << "Value exists: " << *optionalValue << endl;
    } else {
        cout << "Value does not exist." << endl;
    }

    return 0;
}

 

 

위와 같은 코드가 있다. getValue에 들어가는 인자에 따라 적당한 값을 돌려주거나 실패하여 nullptr를 반환한다. main 쪽에서는 해당 값을  보고, 실제 있는 값이라면 출력해 주고, 아니라면 존재하지 않는다고 print 찍어준다.

 

 

위의 코드를 보면 불편한 것이 두 가지 정도 있다.

 

1. 단순 값을 받는 것인데, 실패했다는 것을 nullptr로 표현하기 위하여 return type이 int가 아니라 int* 이다.

2. 위의 이유 때분에 getValue에서 값을 돌려줄 때, 42라는 값을 그대로 돌려주지 못하고 static int를 만든 후, 해당 값의 주소값을 돌려준다.

 

 

그렇다고 return 형태를 int로 바꾼 후, 실패하면 이 값을 보내줄게! 하고 약속하기에는 확장성이 너무 좋지 않다. 

#include <iostream>

constexpr int failValue = -1;

using namespace std;

int getValue(bool cond) {
    if (cond) {
        return 42;
    } 
    /*
    else if (some condition){
        return -1;
    }
    */
    else {
        cout << "error! cannot get value" << endl;
        return failValue;
    }
}

int main() {
    int optionalValue = getValue(true);

    if (optionalValue != failValue) {
        cout << "Value exists: " << optionalValue << endl;
    } else {
        cout << "Value does not exist." << endl;
    }

    return 0;
}

 

만약 -1이라는 값이 getValue 함수 안에 넘겨 줄 필요가 생긴다면? 저 값을 다른 값으로 바꾸어야 할 것이다.

그리고 만약 바꾼 값이 또 getValue 안에서 넘겨 줄 필요가 생긴다면...?

 

 

#include <iostream>
using namespace std;

pair<bool, int> getValue(bool cond) {
    if (cond) {
        return { true, 42 };
    } 
    else {
        cout << "error! cannot get value" << endl;
        return { false, 0 };
    }
}

int main() {
    auto [isSuccess, optionalValue] = getValue(true);

    if (isSuccess == true) {
        cout << "Value exists: " << optionalValue << endl;
    } else {
        cout << "Value does not exist." << endl;
    }

    return 0;
}

 

물론 이런 식으로 구현이 가능하겠지만, 썩 읽기에 좋지는 않다.

 


 

위의 상황을 std::optional을 통해 깔끔하게 해결이 가능해진다.

 

#include <iostream>
#include <optional>
using namespace std;

optional<int> getValue(bool cond) {
    if (cond) {
        return 42;
    } 
    else {
        cout << "error! cannot get value" << endl;
        return std::nullopt;
    }
}

int main() {
    auto optionalValue = getValue(true);

    if (optionalValue) {
        cout << "Value exists: " << *optionalValue << endl;
    } else {
        cout << "Value does not exist." << endl;
    }

    return 0;
}

 

불편하게 pair를 통해 {성공 실패 결과 , 실제 값} 을 넘겨 주지도 않았고, getValue 내부에 쓸데없는 static 변수를 선언하지도 않았다. return 42; 혹은 return std::nullopt; 를 통해서, 밖에서 값을 확인 할 수 있다.

 

또 return type이 optional<int> 이므로, 해당 함수를 사용하는 사용자는 '이 값을 얻어올 때 실패할 가능성도 있겠구나!' 라는 사실을 쉽게 유추할 수 있다.

반응형

'C++ > STL' 카테고리의 다른 글

[C++20] Span  (1) 2023.07.04
[C++] Priority_queue  (0) 2023.06.04
[C++20] Concepts  (1) 2023.05.23
[C++] std::async. launch policy  (0) 2023.04.20
[C++] std::async, std::thread (2)  (0) 2023.04.10