给定一个对象向量,是否有一种优雅的方式来提取它的成员?我目前只是使用for循环,但如果有一种方法可以做到这一点就好了。例如:
#include <vector>
struct Object {
int x;
float y;
};
int main() {
std::vector<Object> obj;
// Fill up obj
std::vector<int> all_x = obj.x; // Won't work obviously
}
std::vector
(或者c++中的其他数据类型),没有一种语法上漂亮的方式来实现你想要的功能。obj
元素的x
成员初始化all_x
,那么你可以定义一个新的迭代器类,像这样: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
然后填充它,使用lambda的std::transform
是一个更清晰的选项(正如@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方法,但提取它将非常困难。
std::transform
。#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;});
}
#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
。#define map(vTarget, vSource, eField) \
for (auto e: vSource) { vTarget.push_back(eField); }
map(names, people, e.name);
让我们试试它们。这是一个完整的例子。嘿,如果有人有更优雅的连接解决方案,我全听着。
#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