拥有以下结构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 /手写等效功能。
答案 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-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;
}
答案 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;
}();
您当然可以使用reserve
和back_inserter
来阻止默认构建,但我实际上是在优先考虑代码之前对代码进行计时。