2016-12-19 3 views
3

을 이름을()를 호출하여 decltype (클래스 : 이름())의 벡터에 클래스의 벡터 변환 std::vector<std::string>.다음 구조체 <code>MyStruct</code>를 갖는 모든 요소에

const boost::range_detail::transformed_range<(lambda at /tmp/main.cpp:21:36), std::vector<MyStruct, std::allocator<MyStruct> > > => const boost::range_detail::transformed_range<(lambda at /tmp/main.cpp:21:36), std::vector<MyStruct, std::allocator<MyStruct> > > 

어떻게 이것을 달성 할 수

#include <iostream> 
#include <string> 
#include <vector> 

#include <boost/range/adaptor/transformed.hpp> 

struct MyStruct { 
    explicit MyStruct(std::string name) : name_(name){} 
    std::string name() const { return name_; } 
private: 
    std::string name_; 
}; 

int main() { 
    std::vector<MyStruct> vm; 
    vm.emplace_back("asd"); 
    vm.emplace_back("qwe"); 
    vm.emplace_back("zxc"); 

    using namespace boost::adaptors; 
    auto vs = vm | transformed([](const MyStruct& c){return c.name();}); 

    for (const auto& c : vs) std::cout << c << std::endl; 
} 

하지만 vs는 벡터하지만이되지 않습니다 : 그래서 같은 그것을 관리해야? 가급적 나는 벡터를 초기화하는 동안 그것을하고 싶습니다 - 벡터를 선언하지 말고 std::copy 또는 비슷한 것을 선언하십시오.

std::vector<std::string> vv; 
vv.reserve(boost::size(vs)); 
boost::copy(vs, std::back_inserter(vv)); 

하지만 난 (바람직 const 한정자) 한 단계 초기화를 싶습니다

가 나는 유사한 무언가를 할 수 있다는 것을 알고.

필자는 필자가 필요로하는 일종의 고차원 (기능적)지도 기능 또는 표준/부스트/필기체 기능이라고 생각합니다.

+0

범위에서'begin'과'end'는 당신에게 한쌍의 반복자를 줄 것입니다. 'std :: vector'는 한 쌍의 반복자를 취하는 생성자를 가지고 있습니다. –

+0

당신은'std :: vector vs; boost :: copy (vm | transformed ([MyStruct & c) {return c.name();}), std :: back_inserter (vs)); 나는 그보다 더 짧은 것을 모른다. – Praetorian

답변

5

기본적으로 새롭고 향상된 Boost.Ranges 인 range-v3을 사용하는 것이 좋습니다. 가, 그냥 : 당신이 auto하여이를 캡처 한 경우, 당신이 vector를 얻을 수 없겠죠

std::vector<std::string> vs = vm | ranges::view::transform(&MyStruct::name); 

하는 것으로. 결과 오브젝트에는 변환 연산자가 있습니다.

Boost.Ranges와

, 당신은 기본적으로 그렇게 할 수는 없지만, 당신은 당신의 자신의 파이프 어댑터를 작성할 수 있습니다

struct make_vector_t 
{ 
    template <class R> 
    friend auto operator|(R&& range, make_vector_t) 
    { 
     using std::begin; 
     using std::end; 

     using value_type = typename iterator_traits<decltype(begin(range))>::value_type; 
     return std::vector<value_type>(begin(range), end(range)); 
    } 
} constexpr make_vector{}; 

를 그냥 사용 :

auto vs = vm 
    | transformed([](const MyStruct& c){return c.name();}); 
    | make_vector; 
+0

굉장합니다. 이 '친구'가 기능 선언의 시작 부분에서 무엇을하는지 설명 할 수 있습니까? – Patryk

+0

@Patryk'make_vector_t'가 오른쪽으로 가고 있기 때문에 멤버가 아닌 함수가 필요합니다. – Barry

+0

나는 아직도 그것을 얻지 못한다. 그 말을 다시 할 수 있니? – Patryk

0

range-v3하면 Barry mentioend로 옵션이 아니므로 멀티 라인 알고리즘을 함수 호출로 바꾸지 않는 이유는 무엇입니까? 예 :

#include <iostream> 
#include <string> 
#include <vector> 
#include <algorithm> 

struct MyStruct { 
    MyStruct(std::string n) : m_name(n) {} 
    std::string name() const { return m_name; } 

    std::string m_name; 
}; 

template <typename T> auto ExtractNames(const std::vector<T> &vm) { 
    std::vector<decltype(std::declval<T>().name())> result(vm.size()); 
    std::transform(vm.begin(), vm.end(), result.begin(), 
       [](const T &v) { return v.name(); }); 

    return result; 
} 

int main() { 
    std::vector<MyStruct> vm{{"asd"}, {"qwe"}, {"zxc"}}; 
    auto vs = ExtractNames(vm); 

    for (auto &elem : vs) { 
    std::cout << elem << "\n"; 
    } 

    return 0; 
} 
+0

여기에서'ExtractNames'에서 반환 된 벡터에서 "생성 된 값을 원하는 값으로 변환하기 전에"기본 구성 값을 지불하고 있습니다. – Patryk

+0

''std :: back_inserter (result)' ''결과''에 대한 계산을 지정하지 않으면 도움이 되겠습니까? –

+0

당신이 선언하고 예약 한 다음 back_inserter를 사용하면 더 좋을 것이라고 생각합니다. – Patryk

1

또한 std::vector<MyStruct>에서 std::vector<std::string>을 구성 할 수 있습니다 std::vector::iterator 주위의 래퍼 역할을하는 헬퍼 클래스를 만들 수 있습니다.

#include <iostream> 
#include <string> 
#include <vector> 

struct MyStruct { 
    explicit MyStruct(std::string name) : name_(name){} 
    std::string name() const { return name_; } 
private: 
    std::string name_; 
}; 

// A minimal wrapper around std::vector::iterator 
// to help with constructing a std::vector<std::string> from a 
// std::vector<MyStruct> 

struct Iter 
{ 
    using iterator_category = std::input_iterator_tag; 
    using value_type = std::string; 
    using pointer = std::string*; 
    using reference = std::string; 
    using difference_type = long; 

    Iter(std::vector<MyStruct>::iterator iter) : iter_(iter) {} 
    std::string operator*() const { return (*iter_).name(); } 
    bool operator!=(Iter const& rhs) const { return this->iter_ != rhs.iter_; } 
    Iter& operator++() { ++iter_; return *this;} 

    std::vector<MyStruct>::iterator iter_; 
}; 

int main() { 
    std::vector<MyStruct> vm; 
    vm.emplace_back("asd"); 
    vm.emplace_back("qwe"); 
    vm.emplace_back("zxc"); 

    std::vector<std::string> vs(Iter(vm.begin()), Iter(vm.end())); 
    for (const auto& c : vs) std::cout << c << std::endl; 
} 
+0

'연산자 ->()'가 작동하지 않습니다. – Barry

+0

'std :: string '을'std :: iterator'의 마지막 매개 변수로 사용했기 때문입니까? –

+0

함수가 참조를 반환하지 않기 때문에'operator ->()'가 작동하지 않는다고 말하는 것 같습니다 –

0

같은 범위-V3를 기반 솔루션으로 좋은,하지만 어떤 제 3 자 라이브러리가 필요하지 할 수있는 장점이있다되지 않음 :

const auto vs = [&]{ 
        std::vector<std::string> t(mv.size()); 
        std::transform(mv.begin(), mv.end(), t.begin(), [](auto& s) {return s.name(); }); 
        return t; 
       }(); 

당신은 물론 기본 건설을 방지하기 위해 reserveback_inserter을 사용할 수 있습니다 ,하지만 실제로는 다른 코드보다 우선적으로 코드를 사용합니다.

관련 문제