C++20 리서치 - STL의 변화[10]
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);
}