给定一个对象矢量,是否有一种优雅的方式来提取其成员?我目前只是使用for循环,但如果有办法可以做到这一点会很好。例如,
class Object {
int x;
float y;
}
vector<Object> obj;
// Fill up obj
vector<int> all_x = obj.x // Won't work obviously
答案 0 :(得分:4)
使用range-v3,它只是
std::vector<int> xs = objs | ranges::view::transform(&Object::x);
或只使用视图:
auto xs = objs | ranges::view::transform(&Object::x);
答案 1 :(得分:3)
由于std::vector
(或者一般来说是c ++)不支持协变聚合,所以没有语法上很好的方法可以做你想要的。
如果您真的想要all_x
成员x
成员初始化 obj
,那么您可以定义一个新的迭代器类,如下所示:
class getx_iter : public vector<Object>::iterator
{
public:
getx_iter(const vector<Object>::iterator &iter) : vector<Object>::iterator(iter) {}
int operator*() { return (*this)->x; }
};
如果你可以初始化一个空vector
然后填充它,std::transform
和一个labmda是一个更清晰的选择(如@andars建议的那样)。
您还可以使用vector::reserve()
和back_inserter
:
xs.reserve(foos.size());
std::transform(foos.begin(), foos.end(), back_inserter(xs), [](Foo f){return f.x;});
另请注意,虽然x
是Object
的私有成员且没有getter,但提取它会非常困难。
答案 2 :(得分:1)
我想不出一个非常好的方式。
另一种方法是将std::transform
与lambda一起使用。
#include <vector>
#include <algorithm>
class Foo {
public:
Foo(int x_): x(x_) {}
int x;
};
int main() {
std::vector<Foo> foos;
for (int i = 0; i<10; i++) {
foos.push_back(Foo(i));
}
std::vector<int> xs;
xs.resize(foos.size());
std::transform(foos.begin(), foos.end(), xs.begin(), [](Foo f){return f.x;});
}
答案 3 :(得分:0)
一些模板和宏魔术,它起作用:
#include <vector>
#include <algorithm>
using namespace std;
class Foo {
public:
Foo(int x_): x(x_) {}
int x;
};
#define GETFIELD(type, field) [](const type & obj){return obj.field;}
template<typename T,typename U, typename TMapper>
void MapVector(vector<T>& src, vector<U>& dst, TMapper mapper) {
for (const auto& it: src) {
dst.push_back(mapper(it));
}
}
#define MapVectorField(src, dst, field) MapVector(src, dst, GETFIELD(decltype(src)::value_type, field))
int main() {
vector<Foo> vFoo;
for (int i = 0; i < 10; i++) {
vFoo.push_back(Foo(i));
}
vector<int> vX;
MapVector(vFoo, vX, GETFIELD(Foo, x));
MapVectorField(vFoo, vX, x);
for (int i = 0; i < vX.size(); i++) {
printf("%d\n", vX[i]);
}
}
当然,请记住,将宏MapVectorField
命名为函数或在生产中编写using namespace std
并不是很好。
答案 4 :(得分:0)
这是另一个非常易于使用的宏。它确实要求程序员知道元素的内部循环变量名称为“ e”。如果您牢记这一点,它将适用于字段或方法,也适用于普通对象或指向对象的指针。
宏很简单:
#define map(vTarget, vSource, eField) \
for (auto e: vSource) { vTarget.push_back(eField); }
例如,假设我们有一个名为Person的类,它具有一个字符串名称字段和一个int age字段,并且我们只想将名称提取到一个新的向量中。这样的用法示例可能是:
map(names, people, e.name);
请注意第三个参数中的“ e”。这是必需的,因为宏使用变量“ e”来迭代向量中的元素。因此,请记住您需要使用哪种语法。我相信这是4种情况:
让我们尝试一下。这是一个完整的例子。嘿,如果有人有一个更优雅的联接解决方案,我会为您所用。
#include <iostream>
#include <string>
#include <vector>
using namespace std;
#define map(vTarget, vSource, eField) \
for (auto e: vSource) { vTarget.push_back(eField); }
class Person {
public:
string name;
int age;
public:
Person(string name, int age) {
this->name=name;
this->age=age;
}
string& getName() {return name;}
int getAge() {return age;}
};
string join(vector<string> vSource, const string separator) {
string buf;
bool first=true;
for (string e: vSource) {
if (first) {
first=false;
} else {
buf+=separator;
}
buf+=e;
}
return buf;
}
int main(int argc, char **argv) {
// using a normal Object
vector<Person> people;
vector<string> names;
people.push_back(Person("john", 27));
people.push_back(Person("jane", 26));
names.clear();
map(names, people, e.name);
cout << join(names, ",") << endl;
names.clear();
map(names, people, e.getName());
cout << join(names, ",") << endl;
// using a pointer to an object
vector<Person*> morePeople;
morePeople.push_back(new Person("bob", 27));
morePeople.push_back(new Person("amy", 26));
names.clear();
map(names, morePeople, e->name);
cout << join(names, ",") << endl;
names.clear();
map(names, morePeople, e->getName());
cout << join(names, ",") << endl;
}
样本输出:
john,jane
john,jane
bob,amy
bob,amy