수색…


소개

선택적 요소 (Maybe 형식이라고도 함)는 내용이 있거나 없을 수도있는 형식을 나타내는 데 사용됩니다. 그것들은 C ++에서 std::optional 클래스로 구현됩니다. 예를 들어, 타입의 객체 std::optional<int> 타입의 어떤 값을 포함 할 수 있습니다 int , 또는 값을 포함 할 수 없습니다.

선택적 요소는 일반적으로 존재하지 않을 수있는 값을 나타내는 데 사용되거나 의미있는 결과를 반환하지 못하는 함수의 반환 형식으로 사용됩니다.

선택 사항에 대한 다른 접근법

std::optional 해결 방법은 포인터를 사용하거나, 센티넬을 사용하거나 pair<bool, T> 사용하여 해결할 수 있습니다.

선택적 vs 포인터

경우에 따라 기존 객체 또는 nullptr 에 대한 포인터를 제공하여 실패를 나타낼 수 있습니다. 그러나 이것은 객체가 이미 존재하는 경우에만 제한됩니다. optional 으로 값 유형으로 optional 으로 메모리 할당을 사용하지 않고 새 객체를 반환 할 수도 있습니다.

옵션 대 센티넬

일반적인 관용구는 값이 의미가 없다는 것을 나타내는 특별한 값을 사용하는 것입니다. 정수형의 경우는 0 또는 -1, 또는 포인터의 경우는 nullptr 가됩니다. 그러나 이것은 유효한 값의 공간을 줄여 주며 (유효한 0과 의미없는 0을 구별 할 수 없으며) 많은 유형이 센티널 값에 대해 자연스럽게 선택되지 않습니다.

선택적 vs std::pair<bool, T>

또 다른 공통 관용구는 한 쌍을 제공하는 것입니다. 요소 중 하나는 그 값이 의미있는 것인지 아닌지를 나타내는 bool 입니다.

이것은 오류의 경우에 기본적으로 구성 가능한 값 유형에 의존합니다. 이는 일부 유형에서는 불가능하며 다른 유형에서는 가능하지만 바람직하지 않습니다. optional<T> 는 오류 발생시 아무 것도 만들 필요가 없다.

옵션 값을 사용하여 값이 없음을 나타냅니다.

C ++ 17 이전에는 nullptr 값을 갖는 포인터를 갖는 것이 일반적으로 값의 부재를 나타 nullptr . 이것은 포인터에 의해 동적으로 할당되고 이미 관리되고있는 대형 객체를위한 좋은 솔루션입니다. 그러나이 솔루션은 포인터와 같이 거의 동적으로 할당되거나 관리되지 않는 int 와 같은 작거나 기본 유형에서는 제대로 작동하지 않습니다. std::optional 은이 일반적인 문제에 대해 실행 가능한 솔루션을 제공합니다.

이 예에서는 struct Person 가 정의됩니다. 애완 동물은 가질 수 있지만 꼭 필요한 것은 아닙니다. 따라서 Personpet 멤버는 std::optional 래퍼로 선언됩니다.

#include <iostream>
#include <optional>
#include <string>

struct Animal {
    std::string name;
};

struct Person {
    std::string name;
    std::optional<Animal> pet;
};

int main() {
    Person person;
    person.name = "John";

    if (person.pet) {
        std::cout << person.name << "'s pet's name is " <<
            person.pet->name << std::endl;
    }
    else {
        std::cout << person.name << " is alone." << std::endl;
    }
}

함수의 실패를 나타내는 옵션을 사용하기

C ++ 17 이전의 함수는 일반적으로 여러 가지 방법 중 하나로 실패를 나타 냈습니다.

  • 널 포인터가 리턴되었습니다.
    • 예를 들어 함수 호출 Delegate *App::get_delegate() 는 delegate가없는 App 인스턴스에서 nullptr 을 반환합니다.
    • 동적으로 할당되었거나 포인터에 의해 크고 관리되는 개체에 대해서는 좋은 솔루션이지만 일반적으로 스택 할당 및 복사를 통해 전달되는 작은 개체에 대해서는 좋은 솔루션이 아닙니다.
  • 반환 유형의 특정 값은 실패를 나타 내기 위해 예약되었습니다.
    • 예를 들어 unsigned shortest_path_distance(Vertex a, Vertex b) 연결되지 않은 두 꼭지점의 unsigned shortest_path_distance(Vertex a, Vertex b) 함수를 호출하면이 사실을 나타내는 0이 반환 될 수 있습니다.
  • 이 값은 bool 과 함께 쌍을 이루어 반환 값이 의미가 있음을 나타냅니다.
    • 정수가 아닌 문자열 인수로 함수 std::pair<int, bool> parse(const std::string &str) 를 호출하면 정의되지 않은 intboolfalse 설정된 쌍을 반환합니다.

이 예에서 John은 두 마리의 애완 동물 인 Fluffy와 Furball을받습니다. 그런 다음 Person::pet_with_name() 함수를 호출하여 John의 애완 동물 수염을 검색합니다. John에게는 Whiskers라는 애완 동물이 없으므로 함수가 실패하고 대신 std::nullopt 가 반환됩니다.

#include <iostream>
#include <optional>
#include <string>
#include <vector>

struct Animal {
    std::string name;
};

struct Person {
    std::string name;
    std::vector<Animal> pets;
    
    std::optional<Animal> pet_with_name(const std::string &name) {
        for (const Animal &pet : pets) {
            if (pet.name == name) {
                return pet;
            }
        }
        return std::nullopt;
    }
};

int main() {
    Person john;
    john.name = "John";
    
    Animal fluffy;
    fluffy.name = "Fluffy";
    john.pets.push_back(fluffy);
    
    Animal furball;
    furball.name = "Furball";
    john.pets.push_back(furball);
    
    std::optional<Animal> whiskers = john.pet_with_name("Whiskers");
    if (whiskers) {
        std::cout << "John has a pet named Whiskers." << std::endl;
    }
    else {
        std::cout << "Whiskers must not belong to John." << std::endl;
    }
}

반환 값으로 선택 사항

std::optional<float> divide(float a, float b) {
  if (b!=0.f) return a/b;
  return {};
}

여기서 우리는 분수 a/b 반환합니다. 그러나 이것이 정의되어 있지 않으면 (무한대가 될 것입니다) 대신 우리는 빈 옵션을 반환합니다.

더 복잡한 경우 :

template<class Range, class Pred>
auto find_if( Range&& r, Pred&& p ) {
  using std::begin; using std::end;
  auto b = begin(r), e = end(r);
  auto r = std::find_if(b, e , p );
  using iterator = decltype(r);
  if (r==e)
    return std::optional<iterator>();
  return std::optional<iterator>(r);
}
template<class Range, class T>
auto find( Range&& r, T const& t ) {
  return find_if( std::forward<Range>(r), [&t](auto&& x){return x==t;} );
}

find( some_range, 7 ) 는 container 또는 range some_range 에서 숫자 7 과 동일한 값을 검색합니다. find_if 는 술어와 함께합니다.

찾지 못하면 빈 선택적 요소를 반환하고, 요소가 있으면 반복 요소를 포함하는 선택 요소를 반환합니다.

이렇게하면 다음을 수행 할 수 있습니다.

if (find( vec, 7 )) {
  // code
}

또는

if (auto oit = find( vec, 7 )) {
  vec.erase(*oit);
}

시작 / 끝 반복자 및 테스트를 사용하지 않아도됩니다.

가치

void print_name( std::ostream& os, std::optional<std::string> const& name ) {
  std::cout "Name is: " << name.value_or("<name missing>") << '\n';
}

value_or 는 선택 항목에 저장된 값을 반환하거나 아무것도 저장하지 않은 경우 인수를 반환합니다.

이것에 의해, 아마 null의 옵션을 취해, 실제로 값이 필요한 경우에 디폴트의 동작을 지정할 수 있습니다. 이런 식으로함으로써, "기본 행동"결정은 어떤 엔진의 배짱이 깊은 곳에서 디폴트 값을 생성하는 대신 가장 잘 만들어지고 즉각적으로 필요한 지점으로 되돌릴 수 있습니다.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow