C++17 구조 화 바 인 딩 의 실현

13010 단어 C++17구조 화귀속
동기
std::map의 insert 방법 은 std::pair을 되 돌려 줍 니 다.두 요 소 는 각각 삽 입 된 키 쌍 의 교체 기와 새로 삽 입 된 요 소 를 표시 하 는 불 값 을 가리 키 고 std::map:iterator 는 인용 을 풀 고 키 쌍 std::pair를 가 져 옵 니 다.std::map 와 관련 된 알고리즘 에서 first 와 second 가 대량으로 나타 날 수 있어 서 당 황 스 럽 습 니 다.

#include <iostream>
#include <map>

int main()
{
  typedef std::map<int, int> Map;
  Map map;
  std::pair<Map::iterator, bool> result = map.insert(Map::value_type(1, 2));
  if (result.second)
    std::cout << "inserted successfully" << std::endl;
  for (Map::iterator iter = map.begin(); iter != map.end(); ++iter)
    std::cout << "[" << iter->first << ", " << iter->second << "]" << std::endl;
}

C++11 표준 라 이브 러 리 에 std::tie 를 추 가 했 습 니 다.약간의 인용 으로 std::tuple 을 구 조 했 습 니 다.std::tuple 대상 은 그 중의 인용 에 일일이 값 을 부여 할 수 있 습 니 다(이원 std::tuple 은 std::pair 구조 또는 할당 할 수 있 습 니 다).std::ignore 는 자리 차지 문자 로 위치 할당 이 무시 되 었 습 니 다.

#include <iostream>
#include <map>
#include <utility>

int main()
{
  std::map<int, int> map;
  bool inserted;
  std::tie(std::ignore, inserted) = map.insert({1, 2});
  if (inserted)
    std::cout << "inserted successfully" << std::endl;
  for (auto&& kv : map)
    std::cout << "[" << kv.first << ", " << kv.second << "]" << std::endl;
}

그러나 이런 방법 은 아직 완벽 하지 않다.왜냐하면:
  • 변 수 는 반드시 사전에 단독으로 성명 해 야 하 며 그 유형 은 모두 명시 적 으로 표시 해 야 하 며 자동 으로 유도 할 수 없다.
  • 기본 구조 함수 가 0 초기 화 를 실행 하 는 유형 에 대해 0 초기 화 과정 은 불필요 합 니 다.
  • std::ofstream 와 같은 기본 구조 함수 가 전혀 없 을 수도 있 습 니 다.
  • 이 를 위해 C++17 은 구조 화 바 인 딩(structured binding)을 도입 했다.
    
    #include <iostream>
    #include <map>
    
    int main()
    {
      std::map<int, int> map;
      auto&& [iter, inserted] = map.insert({1, 2});
      if (inserted)
        std::cout << "inserted successfully" << std::endl;
      for (auto&& [key, value] : map)
        std::cout << "[" << key << ", " << value << "]" << std::endl;
    }
    
    
    구조 화 바 인 딩 이라는 언어 특성 은 제안 단계 에서 분해 성명(decomposition declaration)이 라 고 불 렸 다가 구조 화 바 인 딩 으로 바 뀌 었 다.이 이름 이 강조 하고 자 하 는 것 은 구조 화 된 귀속 의 미 는 성명 이 아니 라 귀속 에 있다 는 것 이다.
    문법
    구조 화 바 인 딩 은 세 가지 문법 이 있 습 니 다.
    
    attr(optional) cv-auto ref-operator(optional) [ identifier-list ] = expression;
    attr(optional) cv-auto ref-operator(optional) [ identifier-list ] { expression };
    attr(optional) cv-auto ref-operator(optional) [ identifier-list ] ( expression );
    
    그 중에서 attr(optional)는 선택 가능 한attributes이 고 cv-auto 는 const 또는 volatile 수식 이 있 을 수 있 는 auto 이 며,ref-operator(optional)는 선택 가능 한&또는&이 며,identifier-list 는 쉼표 로 구 분 된 식별 자 이 며,expression 은 하나의 표현 식 입 니 다.
    또한 initializer 를=expression,{expression}또는(expression)로 정의 합 니 다.다시 말 하면 위의 세 가지 문법 은 통 일 된 형식 attr(optional)cv-auto ref-operator(optional)[identifier-list]initializer 가 있 습 니 다.
    전체 문 구 는 구조 화 된 바 인 딩 성명 으로 식별 자 는 구조 화 바 인 딩(structured bings)이 라 고도 부 르 지만 두 곳 의'binding'의 품사 가 다르다.
    말 나 온 김 에 C++20 에서 volatile 의 많은 용법 이 폐기 되 었 다.
    행위.
    구조 화 바 인 딩 은 세 가지 행위 가 있 고 위의 세 가지 문법 과 대응 하 는 관계 가 없다.
    첫 번 째 경우 expression 은 배열 이 고 identifier-list 의 길 이 는 배열 의 길이 와 같 아야 합 니 다.
    두 번 째 상황,expression 의 유형 E,std::tuplesize는 완전한 유형 으로 E 를 클래스 그룹(tuple-like)유형 이 라 고 합 니 다.STL 에서 std::array,std::pair 와 std::tuple 은 모두 이런 유형 입 니 다.이 때 identifier-list 의 길 이 는 std::tuplesize:value 가 같 고 모든 식별 자의 유형 은 std::tuple 을 통 해element 유도(구체 적 으로 뒷글 참조),구성원 get()또는 get(e)로 초기 화 합 니 다.분명히 이런 표준 라 이브 러 리 시설 은 언어의 핵심 과 연결 되 어 있다.
    세 번 째 상황 에서 E 는 비 유 니 온 류 유형 으로 비 정적 데이터 구성원 을 연결 합 니 다.모든 비정 상 데이터 구성원 은 Public 접근 속성 이 어야 합 니 다.모두 E 에 있 거나 E 의 기본 클래스 에 있어 야 합 니 다(즉,여러 클래스 에 분산 되 어 서 는 안 됩 니 다).identifier-list 는 클래스 의 비정 상 데이터 구성원 의 성명 순서에 따라 연결 되 고 수량 이 같 습 니 다.
    활용 단어 참조
    구조 화 바 인 딩 은 순수 데이터 형식 을 처리 하 는 데 능 합 니 다.사용자 정의 형식 과 std:tuple 등 을 포함 하여 인 스 턴 스 의 모든 필드 에 변 수 를 할당 합 니 다.
    
    #include <iostream>
    
    struct Point
    {
      double x, y;
    };
    
    Point midpoint(const Point& p1, const Point& p2)
    {
      return { (p1.x + p2.x) / 2, (p1.y + p2.y) / 2 };
    }
    
    int main()
    {
      Point p1{ 1, 2 };
      Point p2{ 3, 4 };
      auto [x, y] = midpoint(p1, p2);
      std::cout << "(" << x << ", " << y << ")" << std::endl;
    }
    
    
    다른 문법 사탕 과 함께 현대 C++코드 는 우아 할 수 있 습 니 다.
    
    #include <iostream>
    #include <map>
    
    int main()
    {
      std::map<int, int> map;
      if (auto&& [iter, inserted] = map.insert({ 1, 2 }); inserted)
        std::cout << "inserted successfully" << std::endl;
      for (auto&& [key, value] : map)
        std::cout << "[" << key << ", " << value << "]" << std::endl;
    }
    
    
    구조 화 된 바 인 딩 이 클래스 원 그룹 유형 에 연 결 된 행 위 를 이용 하여 우 리 는 데이터 형식의 구조 화 바 인 딩 디 테 일 을 바 꿀 수 있 습 니 다.이 는 유형 전환,복사 여부 등 을 포함 합 니 다.
    
    #include <iostream>
    #include <string>
    #include <utility>
    
    class Transcript { /* ... */ };
    
    class Student
    {
    public:
      const char* name;
      Transcript score;
      std::string getName() const { return name; }
      const Transcript& getScore() const { return score; }
      template<std::size_t I>
      decltype(auto) get() const
      {
        if constexpr (I == 0)
          return getName();
        else if constexpr (I == 1)
          return getScore();
        else
          static_assert(I < 2);
      }
    };
    
    namespace std
    {
    template<>
    struct tuple_size<Student>
      : std::integral_constant<std::size_t, 2> { };
    
    template<>
    struct tuple_element<0, Student> { using type = decltype(std::declval<Student>().getName()); };
    
    template<>
    struct tuple_element<1, Student> { using type = decltype(std::declval<Student>().getScore()); };
    }
    
    int main()
    {
      std::cout << std::boolalpha;
      Student s{ "Jerry", {} };
      const auto& [name, score] = s;
      std::cout << name << std::endl;
      std::cout << (&score == &s.score) << std::endl;
    }
    Student 는 데이터 형식 으로 두 필드 name 과 score 가 있 습 니 다.name 은 C 스타일 문자열 입 니 다.C 코드 에서 물 려 받 은 것 같 습 니 다.저 는 고객 이 C+스타일 의 std:string 을 사용 하 기 를 바 랍 니 다.score 는 Transcript 유형 으로 학생 들 의 성적 표를 나타 내 는데 이 구조 가 비교적 크 므 로 불필요 한 복사 가 없 도록 const 인용 을 전달 하고 싶 습 니 다.이 를 위해,나 는 세 가지 요 소 를 명확 하 게 썼 다:std:tuplesize、std::tuple_element 와 get.이런 메커니즘 은 구조 화 연결 에 매우 강 한 유연성 을 주 었 다.
    세부 사항
    
    #include <iostream>
    #include <utility>
    #include <tuple>
    
    int main()
    {
      std::pair pair{ 1, 2.0 };
      int number = 3;
      std::tuple<int&> tuple(number);
      const auto& [i, f] = pair;
      //i = 4; // error
      const auto& [ri] = tuple;
      ri = 5;
    }
    
    
    구조 화 바 인 딩 i 가 const auto&로 밝 혀 지면 해당 하 는 유형 이 int 라면 const int&가 아 닐 까요?i = 4;틀 렸 습 니 다.바로 그런 것 같 습 니 다.그러나 리=5 를 어떻게 해석 합 니까?합 법 적 인 건 가요?
    이 문 제 는 체계적으로 처음부터 이야기 해 야 한다.먼저 이름 e,E 를 그 유형 으로 도입 합 니 다.
  • expression 이 배열 형식 A 이 고 ref-operator 가 존재 하지 않 을 때 E 는 cv A 이 며 모든 요 소 는 expression 에 대응 하 는 요소 로 복사(=expression)하거나 직접 초기 화({expression}또는(expression)합 니 다.
  • 그렇지 않 으 면 e 를 attr cv-auto ref-operator e initializer 로 정의 하 는 것 과 같 습 니 다.
  • 즉,괄호 앞의 수식 자 는 모두 e 에 작용 하 는 것 이지 새로운 성명 의 변수 가 아니다.첫 번 째 조 가 왜 독립 되 었 는 지 에 대해 서 는 표준 C+에서 두 번 째 조 의 형식 이 배열 복사 에 사용 되 지 않 기 때문이다.
    그리고 세 가지 상황 으로 나 누 어 토론 한다.
  • 배열 의 경우 E 는 T 의 배열 유형 이 고 모든 구조 화 바 인 딩 은 e 배열 의 요소 의 왼쪽 값 을 가리킨다.인용 유형(referenced type)은 T 이다.구조 화 바 인 딩 은 왼쪽 값 이지 왼쪽 값 참조 가 아 닙 니 다:int array[2]{1,2};auto& [i, j] = array; static_assert(!std::is_reference_v);;
  • 클래스 의 경우 e 가 왼쪽 값 참조 라면 e 는 왼쪽 값(lvalue)이 고 그렇지 않 으 면 소멸 값(xvalue)입 니 다.Ti 를 std::tupleelement:type,구조 화 바 인 딩 vi 의 유형 은 Ti 의 참조 입 니 다.get 이 왼쪽 값 참조 로 돌아 갈 때 왼쪽 값 참조 입 니 다.그렇지 않 으 면 오른쪽 값 참조 입 니 다.인용 유형 은 Ti;decltype 은 구조 화 된 바 인 딩 에 대해 특별한 처 리 를 하고 인 용 된 유형 이 생 성 되 며,클래스 의 경우 구조 화 된 바 인 딩 유형 과 인 용 된 유형 이 다 릅 니 다.
  • 데이터 구성원 의 경우 배열 과 유사 하 며 데이터 구성원 미 를 Ti 형식 으로 설정 하면 구조 화 된 바 인 딩 유형 은 cv Ti 의 왼쪽 값 을 가리 키 는 것 입 니 다(마찬가지 로 왼쪽 값 참조 가 아 닙 니 다).인 용 된 형식 은 cv Ti 입 니 다.
  • 이로써 저 는'구조 화 바 인 딩'의 의 미 는 명확 하 다 고 생각 합 니 다.식별 자 는 항상 한 대상 을 연결 합 니 다.이 대상 은 다른 대상 의 구성원(또는 배열 요소)이 고 후 자 는 복사 하거나 인용(대상 이 아니 라 의 회 를 참조 하면 됩 니 다).인용 과 유사 하 게 구조 화 바 인 딩 은 모두 기 존 대상 의 별명(이 대상 은 암시 적 일 수 있 습 니 다)입 니 다.인용 과 달리 구조 화 바 인 딩 이 꼭 인용 유형 은 아 닙 니 다.
    이제 리 비 const 의 현상 을 설명 할 수 있 습 니 다.컴 파일 러 는 먼저 변수 const auto&e=tuple 을 만 들 었 습 니 다.E 는 const std::tuple&,std::tupleelement<0,E>:type 은 int&,std::get<0>(e)역시 int&로 돌아 가기 때문에 ri 는 int&유형 입 니 다.
    바 텀 을 위 한 C+프로 그래 밍 에 서 는 유 니 온 과 비트 필드(bit field)를 자주 사용 하고 구조 화 바 인 딩 은 이러한 데이터 구성원 을 지원 합 니 다.유 니 온 형식 구성원 이 있 으 면 이름 을 지어 야 합 니 다.바 인 딩 된 식별 자의 유형 은 이 유 니 온 형식의 왼쪽 값 입 니 다.이름 이 없 는 유 니 온 멤버 가 있다 면 이 종 류 는 구조 화 바 인 딩 에 사용 할 수 없습니다.
    C++에는 비트 필드 의 포인터 와 참조 가 존재 하지 않 지만 구조 화 바 인 딩 은 비트 필드 의 왼쪽 값 을 가리 킬 수 있 습 니 다.
    
    #include <iostream>
    
    struct BitField
    {
      int f1 : 4;
      int f2 : 4;
      int f3 : 4;
    };
    
    int main()
    {
      BitField b{ 1, 2, 3 };
      auto& [f1, f2, f3] = b;
      f2 = 4;
      auto print = [&] { std::cout << b.f1 << " " << b.f2 << " " << b.f3 << std::endl; };
      print();
      f2 = 21;
      print();
    }
    
    
    프로그램 출력:
    1 4 3
    1 5 3
    f2 의 기능 은 비트 필드 의 인용 과 같이 원래 의 값 을 쓸 수 있 을 뿐만 아니 라 비트 필드 의 범 위 를 초과 하지 않 습 니 다.
    get 의 이름 찾기,std:tuple 와 같은 문법 디 테 일 도 있 습 니 다.size는 value,explicit 복사 구조 함수 등 이 없습니다.문법 을 깊이 파 는 language lawyer 를 제외 하고 실제 개발 에서 고민 할 필요 가 없습니다.
    제한 하 다.
    상기 코드 예 시 는 모든 유형의 구조 화 바 인 딩 응용 을 포함 하고 있 을 것 입 니 다.당신 이 상상 할 수 있 는 다른 문법 은 모두 틀 렸 습 니 다.포함 되 지만 이에 국한 되 지 않 습 니 다.
    std::initializerlist초기 화;
    std::initializerlist의 길 이 는 동적 이지 만 구조 화 된 바 인 딩 된 식별 자 수 는 정적 입 니 다.
    목록 으로 초기 화―auto[x,y,z]={1,"xyzzy"s,3.14159};
    이것 은 세 개의 변 수 를 설명 하 는 것 과 같 지만 구조 화 된 바 인 딩 의 도 는 성명 이 아니 라 바 인 딩 에 있다.
    설명 하지 않 고 직접 연결 합 니 다―[iter,success]=mymap.insert(value);
    std::tie 를 사용 하 는 것 과 같 으 니 std::tie 를 계속 사용 하 세 요.또한[처음에는 attributes 와 헷 갈 릴 수 있 고 컴 파일 러 와 컴 파일 러 디자이너 에 게 부담 을 줄 수 있 습 니 다.
    구조 화 바 인 딩 의 수식 자―auto[&x,const y,const&z]=f();
    구조 화 된 바 인 딩 에서 벗 어 나 려 는 의도 이기 도 하 다.이러한 기능 이 필요 하거나 변 수 를 하나씩 정의 하거나 세 가지 요 소 를 수 동 으로 쓴다.
    구조 화 바 인 딩 의 유형―SomeClast[x,y]=f()를 가리킨다.또는 auto[x,std::string y]=f();
    첫 번 째 사용 가능 한 auto[x,y]=SomeClass{f()};대신두 번 째 는 이전 조항 과 같다.
    구조 화 된 바 인 딩 을 무시 합 니 다.auto[x,std::ignore,z]=f();
    컴 파 일 러 경 고 를 없 애 는 것 은 하나의 이유 이지 만 auto[x,y,z]=f();void)y;또한이것 은 또 일부 언어 문제 와 관련 이 있 으 니 P0144R 2 3.8 절 로 이동 하 십시오.
    식별 자 끼 워 넣 기--std::tuple,T4>f();auto [ w, [x, y], z ] = f();;
    한 줄 더 쓰 세 요.마찬가지 로 attributes 와 헷 갈 릴 수 있 습 니 다.
    상기 문법 은 C++20 기준 에 포함 되 지 않 았 지만 앞으로 C+문법 확장 이 될 수 있 습 니 다.
    뻗다
    C++17 의 새로운 기능 은 고립 된 것 이 아니 라 구조 화 바 인 딩 과 관련 된 것 은:
    클래스 템 플 릿 매개 변수 유도(class template argument deduction,CTAD)는 구조 함수 매개 변수 로 템 플 릿 매개 변 수 를 유도 합 니 다.
    복사 생략(copy elision),NRV(named return value)의 최 적 화 를 보증한다.
    constexpr if범 형 코드 를 간소화 하고 일부 SFINAE 를 제거한다.
    초기 화 된 조건 부 분기 문:문법 사탕 은 코드 를 더욱 우아 하 게 한다.
    C++17 구조 화 바 인 딩 의 실현 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 C++17 구조 화 바 인 딩 내용 은 예전 의 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 도 많은 응원 부 탁 드 리 겠 습 니 다!

    좋은 웹페이지 즐겨찾기