STL 컨테이너를 필터링하는 현대적인 방법?


103

수년간의 C # 이후 C ++로 돌아와서 나는 현대가 무엇인지 궁금했다.-read : C ++ 11-배열을 필터링하는 방법, 즉이 Linq 쿼리와 비슷한 것을 어떻게 얻을 수 있는가?

var filteredElements = elements.Where(elm => elm.filterProperty == true);

요소 벡터를 필터링하려면 ( strings이 질문을 위해)?

명시적인 메서드 를 정의 boost::filter_iterator해야하는 이전 STL 스타일 알고리즘 (또는 같은 확장 )이 지금 대체 되기를 진심으로 바랍니다 .


filterProperty설정된 모든 요소를 ​​검색합니까 true?
Joseph Mansfield

네, 죄송합니다. 일부 일반 필터 기준 ..
ATV

3
.NET의 LINQ 메서드를 에뮬레이트하려는 라이브러리도 있습니다 : Linq ++cpplinq . 나는 그들과 함께 일하지 않았지만 STL 컨테이너를 지원한다고 생각합니다.
Dirk

1
C ++와 C # 모두에 능숙한 사람들이 적기 때문에 원하는 것이 무엇인지 더 명확해야합니다. 원하는 작업을 설명하십시오.
Yakk-Adam Nevraumont 2014 년

답변:


125

cplusplus.com의 예를 참조하십시오 std::copy_if.

std::vector<int> foo = {25,15,5,-5,-15};
std::vector<int> bar;

// copy only positive numbers:
std::copy_if (foo.begin(), foo.end(), std::back_inserter(bar), [](int i){return i>=0;} );

std::copy_iffoo여기 에있는 모든 요소에 대해 람다 식을 평가하고 반환 true하면 값을에 복사합니다 bar.

을 사용 하면 필요한 크기로 먼저 크기를 조정할 필요없이 반복기를 사용하여 (사용하여 ) std::back_inserter끝에 새 요소를 실제로 삽입 할 수 있습니다 .barpush_back()


30
이것이 C ++이 제공해야하는 LINQ에 정말 가장 가깝습니까? 이것은 열망 (게으르지 않은 IOW)이며 매우 장황합니다.
usr

1
@usr 그것의 IMO 구문 설탕, 간단한 for 루프는 일을 잘 수행합니다 (그리고 종종 복사를 피할 수 있습니다).
Sebastian Hoffmann

1
OPs 예제는 LINQ 구문 설탕을 사용하지 않습니다. 이점은 게으른 평가와 구성 가능성입니다.
usr

1
@usr 여전히 쉽게에 대한 루프 간단한 의해 달성 될 수있는 std::copy_ifA의 루프 이하입니다
세바스찬 호프만

15
@Paranaix 모든 것은 어셈블리에 대한 통사론 적 설탕이라고 할 수 있습니다. 요점은 for 루프를 작성하는 것이 아니라, 알고리즘이 기본 연산 (필터와 같은)을 사용하여 읽기 쉬운 방식으로 명확하게 구성 될 수있는 경우입니다. 많은 언어가 이러한 기능을 제공합니다. C ++에서는 불행히도 여전히 복잡합니다.
BartoszKP 2014

48

목록의 새 사본이 실제로 필요하지 않은 경우보다 효율적인 방법은이며 remove_if, 이는 실제로 원래 컨테이너에서 요소를 제거합니다.


7
@ATV 나는 remove_if완전히 새로운 목록을 복사하는 것보다 더 빠른 돌연변이가있을 때 필터를 사용하는 방법이기 때문에 특히 좋아 합니다. C ++에서 필터를 사용하는 경우을 통해 이것을 사용 copy_if하므로 추가한다고 생각합니다.
djhaskin987

16
벡터를 들어, 적어도 remove_if를 변경하지 않습니다 size(). 당신은 그것을 erase위해 그것을 연결해야 합니다 .
rampion

5
@rampion 예 .. 지우기 / 제거. 요즘 C ++로 작업 할 때 테이프에 구멍을 뚫는듯한 느낌을주는 또 다른 아름다움 (현대 언어와 달리) ;-)
ATV

1
명시 적 지우기는 기능입니다. 모든 경우에 지울 필요는 없습니다. 때때로 반복자는 계속하기에 충분합니다. 이러한 경우 암시 적 지우기는 불필요한 오버 헤드를 생성합니다. 또한 모든 컨테이너의 크기를 조정할 수있는 것은 아닙니다. 예를 들어 std :: array에는 지우기 방법이 전혀 없습니다.
Martin Fehrs

35

C ++ 20에서는 범위 라이브러리의 필터보기를 사용합니다. (필요 #include <ranges>)

// namespace views = std::ranges::views;
vec | views::filter([](int a){ return a % 2 == 0; })

에서 짝수 요소를 lazily 반환합니다 vec.

( [range.adaptor.object] / 4[range.filter] 참조 )


이것은 이미 GCC 10 ( 라이브 데모 )에서 지원됩니다 . Clang 및 이전 버전의 GCC의 경우 원래 range-v3 라이브러리도 ( 라이브 데모 ) 대신 #include <range/v3/view/filter.hpp>(또는 #include <range/v3/all.hpp>) 및 ranges::views네임 스페이스 와 함께 사용할 수 있습니다 .std::ranges::views


#include를 제공하고 컴파일에 대한 답변에 필요한 네임 스페이스를 사용해야합니다. 또한 오늘날 어떤 컴파일러가 이것을 지원합니까?
gsimard

2
@gsimard 더 나은 지금?
LF

2
누군가 macOS에서이 작업을 시도하는 경우 : 2020 년 5 월 현재 libc ++ 는이를 지원하지 않습니다.
dax

25

Boost.Range 도 언급 할 가치가 있다고 생각 합니다. 결과 코드는 원본과 매우 유사합니다.

#include <boost/range/adaptors.hpp>

// ...

using boost::adaptors::filtered;
auto filteredElements = elements | filtered([](decltype(elements)::value_type const& elm)
    { return elm.filterProperty == true; });

유일한 단점은 람다의 매개 변수 유형을 명시 적으로 선언해야한다는 것입니다. decltype (elements) :: value_type을 사용했습니다. 왜냐하면 정확한 유형을 철자 할 필요가없고 일반성을 추가하기 때문입니다. 또는 C ++ 14의 다형성 람다를 사용하여 유형을 간단히 auto로 지정할 수 있습니다.

auto filteredElements = elements | filtered([](auto const& elm)
    { return elm.filterProperty == true; });

filterElements는 순회에 적합한 범위이지만 기본적으로 원래 컨테이너의보기입니다. 필요한 것이 기준을 충족하는 요소의 복사본으로 채워진 다른 컨테이너 인 경우 (원래 컨테이너의 수명과 독립적 임) 다음과 같을 수 있습니다.

using std::back_inserter; using boost::copy; using boost::adaptors::filtered;
decltype(elements) filteredElements;
copy(elements | filtered([](decltype(elements)::value_type const& elm)
    { return elm.filterProperty == true; }), back_inserter(filteredElements));

12

C #과 동등한 C ++ 제안

var filteredElements = elements.Where(elm => elm.filterProperty == true);

필터링을 수행하기 위해 람다 조건자를 전달하는 템플릿 함수를 정의합니다. 템플릿 함수는 필터링 된 결과를 반환합니다. 예 :

template<typename T>
vector<T> select_T(const vector<T>& inVec, function<bool(const T&)> predicate)
{
  vector<T> result;
  copy_if(inVec.begin(), inVec.end(), back_inserter(result), predicate);
  return result;
}

사용하기-간단한 예를 제공합니다.

std::vector<int> mVec = {1,4,7,8,9,0};

// filter out values > 5
auto gtFive = select_T<int>(mVec, [](auto a) {return (a > 5); });

// or > target
int target = 5;
auto gt = select_T<int>(mVec, [target](auto a) {return (a > target); });

11

밑줄 d 제안에 따라 pjm 코드가 개선 되었습니다 .

template <typename Cont, typename Pred>
Cont filter(const Cont &container, Pred predicate) {
    Cont result;
    std::copy_if(container.begin(), container.end(), std::back_inserter(result), predicate);
    return result;
}

용법:

std::vector<int> myVec = {1,4,7,8,9,0};

auto filteredVec = filter(myVec, [](int a) { return a > 5; });
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.