通过在每个元素上调用name(),将Class的向量转换为decltype(Class :: name())的向量

时间:2016-12-19 17:11:28

标签: c++ c++11 vector boost

拥有以下结构MyStruct

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

我想将std::vector MyStruct转换为std::vector<std::string>。我设法这样做了:

#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不是矢量而是:

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> > >

我怎样才能做到这一点?我希望在初始化向量时这样做 - 不是通过声明向量然后是std::copy或类似的东西。

我知道我可以做类似的事情:

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

但我想进行一步初始化(最好使用const限定符)。

我相信这是我需要的某种高阶(功能)地图功能或std / boost /手写等效功能。

4 个答案:

答案 0 :(得分:5)

我建议使用range-v3,这基本上是新的和改进的Boost.Ranges。那里,它只是:

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

请注意,如果您通过auto抓取了此内容,则不会获得vector。生成的对象具有转换运算符。

使用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;

答案 1 :(得分:1)

您还可以创建一个辅助类,作为std::vector::iterator的包装,可用于从std::vector<std::string>构建std::vector<MyStruct>

#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;
}

答案 2 :(得分:0)

如果range-v3Barry 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;
}

答案 3 :(得分:0)

不如基于range-v3的解决方案好,但它的优点是不需要任何第三方库:

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来阻止默认构建,但我实际上是在优先考虑代码之前对代码进行计时。