C++20에 추가된 STL 표준 관련 추가된 기능을 살펴보겠습니다.

STL Container에 추가된 멤버 함수

  • starts_with(), end_with()
#include <iostream>
#include <string>
#include <string_view>

int main()
{
    std:;string s = "fileName.txt";
    std::string_view sv = s;
    
    bool b1 = s.starts_with("hello");  // 어떤 문자열로 시작하는가 : false
    bool b2 = s.end_with(".txt");      // 어떤 문자열로 끝나는가 : true
    
    bool b1 = sv.starts_with("hello"); // false
    bool b2 = sv.end_with(".txt");     // true
}
  • contains()
#include <iostream>
#include <set>

int main()
{
    std::set<int> s = {1, 2, 3, 4, 5};
    
    /* 연관 컨테이너에 요소가 있는지 조사 방법 */
    
    // C++17 이전 방식
    auto ret == s.find(3);
    if ( ret != s.end() ) {}
    
    // C++20 추가 방식
    if(ss.contrains(3)) //true
    {
        std::cout << "exist 3" << endl;
    }
}
  • find() 보다 사용이 쉽고 직관적이며, 연관 컨테이너 8개에 모두 추가되었습니다. set, multiset, map, multimap, unordered_set, unordered_multiset, unordered_map, unordered_multimap

Value_type 관련 변화

  • iterator의 타입 관련 예제
template<typename T> void foo(const T& first)
{
    // 반복자가 가르키는 타입의 변수 생성하기
    // C++98 방식
    typename T::value_type n;
    typename std::iterator_traits<T>::value_type n1;
    
    // C++20 방식
    std::iter_value_t<T> n2;
    
    std::cout << typeid(n1).name() << std::endl;
    std::cout << typeid(n2).name() << std::endl;
}

typename<typename C> void goo(const C& cont)
{
    // 컨테이너가 저장하는 타입 구하기
    // C++98 방식
    // typename C::value_type n; // C가 배열이면 Error
    
    // C++20 방식
    std::ranges::iterator_t<C> it;
    std::ranges::range_value_t<C> n1;
    
    std::cout << typeid(n1).name() << std::endl;
}

int main()
{
	std::vector<int> v = { 1, 2, 3, 4, 5 };
    int x[5] = { 1, 2, 3, 4, 5 };
    
    foo(std::begin(v));
    foo(std::begin(x));
    
    goo(v);
    goo(x);
}
  • 활용 예제
#include <iostream>
#include <vector>
#include <type_traits>

template<typename T> class MyList
{
public:
	List(std::size_t sz, T v) {}    
    
    template<typename C>
    List(C&& c) {}
};
// C++17 class template type deduction
// template<typename C> List< typename std::remove_reference_t<C>::vlaue_type >;

// C++ 20 방식 type deduction
template<typename C> List< std::rangess::range_value_t<C> >;

int main()
{
    MyList<int> s1(10, 3); // C++14 이전까지는 템플릿 인자 전달 필요
    MyList	    s2(10, 3); // C++17 이후부터 OK
    
    std::vector v = {1, 2, 3};
    List s3(v); // type deduction으로 컴파일러가 인자를 추론하여 대입 가능
}

신규 STL 알고리즘

  • std::erase, std::erase_if
int main()
{
    // C++98 remove
    // remove시 실제 제거를 하는 것이 아닌 요소를 앞으로 당겨놓으며 vector의 size는 변화가 없음 (실제 제거하려면 erase가 필요)
    std::vector<int> v1 = { 1, 2, 3, 4, 5 };  
    auto p = std::remove_if(v1.begin(), v1.end(), [](int n){ return n % 2 == 0; });
    v1.erase(p, v1.end());
  
    // C++20 erase
    // 함수 인자로 반복자가 아닌 컨테이너를 직접 전달하며 erase시 실제 컨테이너의 size 변경되며 대상이 제거됨
    std::vector<int> v2 = { 1, 2, 3, 4, 5 };
    std::erase(v2, 3); // 3을 제거해 달라
    std::erase(v2.begin(), v2.end(), 3); // Error 불가
    std::erase_if(v2, [](int n){ return n % 2 == 0; });
}
  • std::shift_left, std::shift_right
int main()
{
    std::vector<int> v1 = { 1, 2, 3, 4, 5, 6, 7, 8 };  
    
    std::shift_left(std::begin(v1), std::end(v1), 3); // v1 -> 4, 5, 6, 7, 8, 6, 7, 8
    std::shift_right(std::begin(v1), std::end(v1), 2); // v1 -> 4, 5, 4, 5, 6, 7, 8
}
  • 정수간 비교 관련 함수 -> signed, unsigned 간 정수 비교시 의도 하지 않은 결과 예방
#include <iostream>
#include <utility>

int main()
{
    int sn = 0;
    unsigned int un = 0;
    
    std::cout << ( -1 < sn ) << std::endl; // true
    std::cout << ( -1 < sn ) << std::endl; // false -> true가 나오는 것을 기대 할 텐데 이런 경우 이슈가 될 수 있다.
    
    std::cout << std::cmp_less( -1, un ) << std::endl; // true : 의도한 결과 출력
    
    // 관련 추가된 함수
    // cmp_equal, cmp_not_equal
    // cmp_less, cmp_greater
    // cmp_less_equal, cmp_greater_equal
}
  • make_shared 관련 변화
#include <iosteam>
#include <memory>

struct Point2 {int x, y; };

int main()
{
    // 배열 버전 추가 관련 예제
    std::shared_ptr<int> sp1( new int );
    std::shared_ptr<int> sp2 = std::make_shared<int>();
    
    std::shared_ptr<int[]> sp3( new int[10] );
    std::shared_ptr<int[]> sp4 = std::make_shared<int[10]>(); // C++20 부터 make_shared 배열 버전 추가
    
    auto sp5 = std::make_shared<int[]>(3);  // new int[3]
    auto sp6 = std::make_shared<int[3]>();  // new int[3]
    auto sp7 = std::make_shared<int[3]>(4); // new int[3]{4, 4, 4, 4}; 초기화 값 명시 가능
    
    // 초기값 관련 예제
    std::shared_ptr<Point2> sp1 = std::make_shared<Point2>(); // C++17 이전 : 내부적으로 기본값 0으로 초기화 해줌
    
    std::shared_ptr<Point2[]> sp2 = std::make_shared_for_overwrite<Point2>[](3); // C++20 : 배열 버전 make_shared의 구조체 초기값 지정 가능
}
  • to_array, midpoint
#include <iostream>
#include <array>

int main()
{
    // to_array 예제
    auto a1 = std::to_array(x);        // std::array<int, 10>
    auto a2 = std::to_array("foo");    // std::array<char, 4>
    auto a3 = std::to_array({1, 2, 3}) // std::array<int, 3>
        
    // midpoint 예제 : 두 수의 중간값을 리턴
    std::cout << std::midpoint(1, 3) << std::endl;
    std::cout << std::midpoint(1.3, 3.4) << std::endl;
}
  • bind_front
#include <iostream>
#include <fuctional>

void foo(int a, int b, int c) { printf("%d %d %d\n", a, b, c); }

int main()
{
    auto f1 = std::bind(&foo, 3, std::placeholders::_1, std::placeholders::_2); // C++11
    
    /* C++ 20 bind_front : 인자 재배치는 불가 */
    auto f2 = std::bind_front(&foo, 3); // 첫번째 인자 3으로 고정
    auto f3 = std::bind_front(&foo, 3, 4); // 첫번째 인자 3, 두번째 인자 4로 고정
    auto f4 = std::bind_front(&foo, 3, 4, 5); // 인자 3, 4, 5로 고정
    
    f2(1, 2);
    f3(1);
    f4();
}
  • span
#include <iostream>
#include <vector>
#include <span>

//void foo(int* arr)
void foo( std::span<int> sp ) // span : 연속된 메모리의 시작 주소와 개수를 관리, list 등은 전달 불가
{
    std::cout << sp.size() << std::endl;
    
    sp[0] = 10;
    
    auto p = std::as_bytes(sp);
    p[3] = static_cast<std::byte>(0x33); // error as_bytes의 리턴값은 읽기 전용
    
    auto p2 = std::as_writable_bytes(sp);
    p2[3] = static_cast<std::byte>(0x33); // OK
    
    std::cout << std::hex << sp[0]  << std::endl;
}

int main()
{
    int x[5]{1, 2, 3, 4, 5 };
    std::vector<int> x2{1, 2, 3, 4, 5 };
    std::span<int> x3(x);
    std::span<int, 10> x4(x);
    
    foo(x);
    foo(x2);
}

더 많은 C++20 관련 정보