728x90

1. std::source_location을 이용한 로그 작성

  • 기존 __FILE__, __LINE__ 매크로를 사용한 로그 방식을 개선하고자 추가된 내용이다.

 

2. 사용 방법

void log( std::string_view InMsg, const std::source_location& InLocation = std::source_location::current() )
{
    std::cout << InMsg << InLocation.file_name() << " " << InLocation.line() << std::endl;
}

int main()
{
    // C++ 20에 추가된 std::source_location을 이용한 로그 작성
    log( "Error : " );

    // 기존 __FILE__, __LINE__ 매크로를 이용한 로그 작성
    std::cout << "Error : " << __FILE__ << " " << __LINE__ << std::endl;
}
  • 매크로 방식에서, 호출 지점의 정보를 담은 객체를 사용한 방식으로 개선되었다.
  • std::source_location 클래스는 소스 파일 이름, 행 번호, 열 번호, 함수 이름과 같은 정보를 제공한다.

 

3. std::source_location::current() 정의

_STD_BEGIN
struct source_location {
    _NODISCARD static consteval source_location current(const uint_least32_t _Line_ = __builtin_LINE(),
        const uint_least32_t _Column_ = __builtin_COLUMN(), const char* const _File_ = __builtin_FILE(),
        const char* const _Function_ = __builtin_FUNCTION()) noexcept {
        source_location _Result;
        _Result._Line     = _Line_;
        _Result._Column   = _Column_;
        _Result._File     = _File_;
        _Result._Function = _Function_;
        return _Result;
    }

    _NODISCARD_CTOR constexpr source_location() noexcept = default;

    _NODISCARD constexpr uint_least32_t line() const noexcept {
        return _Line;
    }
    _NODISCARD constexpr uint_least32_t column() const noexcept {
        return _Column;
    }
    _NODISCARD constexpr const char* file_name() const noexcept {
        return _File;
    }
    _NODISCARD constexpr const char* function_name() const noexcept {
        return _Function;
    }
  ...
  ...
 }
  • std::source_location::current()는 호출 지점의 정보를 담은 객체를 생성한다.

 

[참고]

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

반응형

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

[C++ 20] std::erase_if 를 이용한 요소 삭제  (0) 2022.02.25
[C++ 20] std::span  (0) 2022.02.25
[C++ 20] 템플릿 람다  (0) 2022.02.23
[C++ 20] consteval, constinit 지정자  (0) 2022.02.23
[C++20] 지명 초기화  (0) 2022.02.22
728x90
 

[C++] std::remove_if 를 이용한 요소 삭제

1. std::remove_if를 사용한 요소 삭제 std::vector< int > v1 = { -2, -1, 0, 1, 2 }; std::remove_if( v1.begin(), v1.end(), []( int InNum ) { return InNum < 0; } ); // 출력 0 1 2 1 2 for ( auto& num : v..

ggoom.tistory.com

 

1. std::erase_if를 이용한 요소 삭제

  • C++ 20 이전에는 std::remove_if와 erase를 이용한 방법으로 요소를 삭제해왔다.
  • 두 번의 함수 호출과, 반복자를 지정해야 한다는 불편함을 개선하고자 추가된 내용이다.

 

2. 사용 방법

    std::vector< int > v1 = { -2, -1, 0, 1, 2 };
 
    // 요소 0 삭제
    std::erase( v1, 0 );

    // 0 미만 삭제
    std::erase_if( v1, []( int InNum ) { return InNum < 0;  } );

    // 출력 1 2
    for ( auto num : v1 )
        std::cout << num << " ";
  • 기존 std::remove_if를 통해 반복자를 얻고, erase를 통해 삭제하는 방식에서 std::erase_if 호출 한 번으로 개선되었다.

 

3. std::erase_if 정의

template <class _Container, class _Pr>
_CONSTEXPR20 typename _Container::size_type _Erase_remove_if(_Container& _Cont, _Pr _Pred) {
    // erase each element satisfying _Pred
    auto _First          = _Cont.begin();
    const auto _Last     = _Cont.end();
    const auto _Old_size = _Cont.size();
    _Seek_wrapped(_First, _STD remove_if(_Get_unwrapped(_First), _Get_unwrapped(_Last), _Pred));
    _Cont.erase(_First, _Last);
    return _Old_size - _Cont.size();
}
  • 정의로 이동하게 되면, 함수 내부에서 remove_if를 호출하고 erase까지 호출하는 내용을 확인해볼 수 있다.
  • 기존 erase-remove 방식을 그대로 차용하되, 사용자 편의를 개선한 내용임을 확인할 수 있는 부분이다.
반응형

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

[C++ 20] std::source_location을 이용한 로그 작성  (0) 2022.02.26
[C++ 20] std::span  (0) 2022.02.25
[C++ 20] 템플릿 람다  (0) 2022.02.23
[C++ 20] consteval, constinit 지정자  (0) 2022.02.23
[C++20] 지명 초기화  (0) 2022.02.22
728x90

1. std::remove_if를 사용한 요소 삭제

    std::vector< int > v1 = { -2, -1, 0, 1, 2 };
    std::remove_if( v1.begin(), v1.end(), []( int InNum ) { return InNum < 0;  } );

    // 출력 0 1 2 1 2
    for ( auto& num : v1 )
        std::cout << num << " ";
  • std::remove_if를 사용해 얻고자 한 결과는 크기가 3인 { 0, 1, 2 } 벡터지만, 결과는 크기가 줄어들지 않았다.
  • 이유는 std::remove_if는 삭제할 요소를 컨테이너 뒤쪽으로 옮기고, 새로운 반복자를 반환하는 함수이기 때문이다.
  • 결괏값이 { 0, 1, 2, -1, -2 }가 아닌 것은, 조건이 일치한 반복자를 일치하지 않는 반복자 위치에 덮어쓰기 때문이다.

 

2. erase - remove를 이용한 요소 제거

    // 예시 1번
    std::vector< int > v1 = { -2, -1, 0, 1, 2 };
    auto newEnd = std::remove_if( v1.begin(), v1.end(), []( int InNum ) { return InNum < 0;  } );

    // 획득한 반복자를 통해 요소 삭제
    v1.erase( newEnd, v1.end() );

    // 출력 0 1 2
    for ( auto& num : v1 )
        std::cout << num << " ";
    // 예시 2번
    std::vector< int > v1 = { -2, -1, 0, 1, 2 };
 
    // erase - remove 
    v1.erase( std::remove_if( v1.begin(), v1.end(), []( int InNum ) { return InNum < 0;  } )
              , v1.end() );

    // 출력 0 1 2 1 2
    for ( auto& num : v1 )
        std::cout << num << " ";
  • std::remove_if 함수를 통해, 삭제가 진행되어야 할 반복자를 얻을 수 있다.

3. 설명

-2 -1 0 1 2
삭제 -1 0 1 2
-1 0 1 2 X
  • 배열과 같이 특정 요소를 삭제하게 될 경우, 뒤에 있는 요소들을 이동시켜야 하는 연산 과정이 필요하다.
  • 단순 삭제 연산보다 요소들을 이동시키는 연산이 크고, 뒤에 불필요한 공간이 남는 불편함이 존재함
  • std::remove_if는 삭제할 요소들을 삭제하지 않을 요소들과 위치를 바꿔 불필요한 이동 연산을 진행하지 않음
  • erase 함수를 통해 일괄적으로 요소들을 삭제하기 때문에, 잦은 삭제로 인한 이동 연산이 필요 없어진다.

 

4. C++20 std::erase, std::erase_if

  • erase-remove 관용구처럼 사용되던 내용이 std::erase, std::erase_if로 사용할 수 있게 추가되었다.
 

[C++ 20] std::erase_if 를 이용한 요소 삭제

v1 = { -2, -1, 0, 1, 2 }; std::remove_if( v1.begin(), v1.end(), []( int InNum ) { return InNum < 0; } ); // 출력 0 1 2 1 2 for ( auto& num : v.." data-og-host="ggoom.tistory.com" data-og-source-url=..

ggoom.tistory.com

 

[참고]

https://en.cppreference.com/w/cpp/algorithm/remove

반응형

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

[C++] 주석 팁  (1) 2022.02.19
728x90

1. std::span 정의

  • 연속 객체 순차열을 참조하는 객체.
  • C 배열, 포인터와 크기, std::array, std::vector, std::string 등 을 연속 객체라 부른다.
  • 크기가 컴파일 시점에 알려지며, 해당 형식 자체에 크기 정보가 포함되었으면 정적 길이를 가진다고 부른다.
  • 크기가 실행 시점에 결정되면 동적 길이를 가진다고 부른다.

 

2. std::span 선언

template <class _Ty, size_t _Extent = dynamic_extent>
class span : private _Span_extent_type<_Ty, _Extent> {
private:
    using _Mybase = _Span_extent_type<_Ty, _Extent>;
    ...
    ...
  • std::span 정의를 확인해보면 타입 형식과 사이즈를 받고 있다.

 

3. std::span 사용 방법

    // 크기가 실행 시점에 정해지므로, 동적 길이를 가진 std::span
    std::vector < int > v1 = { 1, 2, 3, 4, 5, 6 };
    std::span< int > dynamicSpan( v1 );

    // 컴파일 시점에 크기가 6으로 정해지므로, 정적 길이를 가진 std::span
    std::vector < int > v2 = { 1, 2, 3, 4, 5, 6 };
    std::span< int, 6 > staticSpan( v2 );

    // 포인터와 크기로 생성
    std::vector < int > v3 = { 1, 2, 3, 4, 5, 6 };
    std::span pointerSpan( v3.data(), v3.size() );

 

4. std::span 도입 배경

void printArr( std::span< int > InArr )
{
    // std::span을 사용하게 되어 배열 크기를 확인할 수 있게되었다.
    std::cout << InArr.size() << std::endl;
    for ( auto num : InArr )
        std::cout << num << std::endl;
}

int main()
{
    // C 배열 ( 연속 객체 )
    int arr[] = { 1, 2, 3, 4 };
    printArr( arr );

    // 연속 객체
    std::vector< int > arr1 = { 1, 2, 3, 4 };
    printArr( arr1 );
}
  • C 배열을 함수로 전달하면, 크기 정보가 없는 포인터로 변하게 되므로 잦은 오류를 발생하게 되어 도입되었다.

 

반응형

+ Recent posts