虚拟地将结构向量转换为结构成员的向量
Virtually turn vector of struct into vector of struct members
我有一个接受类似矢量的输入的函数。为了简化事情,让我们使用此print_in_order
函数:
#include <iostream>
#include <vector>
template <typename vectorlike>
void print_in_order(std::vector<int> const & order,
vectorlike const & printme) {
for (int i : order)
std::cout << printme[i] << std::endl;
}
int main() {
std::vector<int> printme = {100, 200, 300};
std::vector<int> order = {2,0,1};
print_in_order(order, printme);
}
现在我有一个vector<Elem>
,想为向量中的每个 Elem 打印一个整数成员Elem.a
。我可以通过创建一个新vector<int>
(为所有 Elems 复制a
(并将其传递给 print 函数来做到这一点 - 但是,我觉得必须有一种方法可以传递一个"虚拟"向量,当在其上使用operator[]
时,仅返回成员a
。请注意,我不想更改访问成员的print_in_order
函数,它应该保持通用。
这可能吗,也许用 lambda 表达式? 完整代码如下。
#include <iostream>
#include <vector>
struct Elem {
int a,b;
Elem(int a, int b) : a(a),b(b) {}
};
template <typename vectorlike>
void print_in_order(std::vector<int> const & order,
vectorlike const & printme) {
for (int i : order)
std::cout << printme[i] << std::endl;
}
int main() {
std::vector<Elem> printme = {Elem(1,100), Elem(2,200), Elem(3,300)};
std::vector<int> order = {2,0,1};
// how to do this?
virtual_vector X(printme) // behaves like a std::vector<Elem.a>
print_in_order(order, X);
}
直接做你想做的事是不可能的。相反,您可能希望从标准算法库中获取提示,例如std::for_each
获取一个额外的参数,该参数是为每个元素调用的类似函数的对象。然后,您可以轻松传递仅打印所需元素的 lambda 函数。
也许像
template<typename vectorlike, typename functionlike>
void print_in_order(std::vector<int> const & order,
vectorlike const & printme,
functionlike func) {
for (int i : order)
func(printme[i]);
}
然后像这样称呼它
print_in_order(order, printme, [](Elem const& elem) {
std::cout << elem.a;
});
由于C++函数重载,您仍然可以保留普通向量的旧print_in_order
函数。
使用成员指针,您可以实现一个代理类型,该类型允许您通过将每个对象替换为其成员之一(请参阅指向数据成员的指针(或其中一个 getter(请参阅指向成员函数的指针(来查看对象的容器。第一个解决方案仅处理数据成员,第二个解决方案同时处理这两个解决方案。
容器必然需要知道要使用哪个容器以及要映射哪个成员,这将在构建时提供。指向成员的指针的类型取决于该成员的类型,因此必须将其视为附加模板参数。
template<class Container, class MemberPtr>
class virtual_vector
{
public:
virtual_vector(const Container & p_container, MemberPtr p_member_ptr) :
m_container(&p_container),
m_member(p_member_ptr)
{}
private:
const Container * m_container;
MemberPtr m_member;
};
接下来,实现operator[]
运算符,因为您提到这是您希望访问元素的方式。取消引用成员指针的语法起初可能令人惊讶。
template<class Container, class MemberPtr>
class virtual_vector
{
public:
virtual_vector(const Container & p_container, MemberPtr p_member_ptr) :
m_container(&p_container),
m_member(p_member_ptr)
{}
// Dispatch to the right get method
auto operator[](const size_t p_index) const
{
return (*m_container)[p_index].*m_member;
}
private:
const Container * m_container;
MemberPtr m_member;
};
要使用此实现,您需要编写如下内容:
int main() {
std::vector<Elem> printme = { Elem(1,100), Elem(2,200), Elem(3,300) };
std::vector<int> order = { 2,0,1 };
virtual_vector<decltype(printme), decltype(&Elem::a)> X(printme, &Elem::a);
print_in_order(order, X);
}
这有点麻烦,因为没有发生模板参数推断。因此,让我们添加一个自由函数来推导模板参数。
template<class Container, class MemberPtr>
virtual_vector<Container, MemberPtr>
make_virtual_vector(const Container & p_container, MemberPtr p_member_ptr)
{
return{ p_container, p_member_ptr };
}
用法变为:
int main() {
std::vector<Elem> printme = { Elem(1,100), Elem(2,200), Elem(3,300) };
std::vector<int> order = { 2,0,1 };
auto X = make_virtual_vector(printme, &Elem::a);
print_in_order(order, X);
}
如果要支持成员函数,则稍微复杂一些。首先,取消引用数据成员指针的语法与调用函数成员指针略有不同。您必须实现两个版本的operator[]
,并根据成员指针类型启用正确的版本。幸运的是,该标准提供了std::enable_if
和std::is_member_function_pointer
(都在<type_trait>
标题中(,这使我们能够做到这一点。成员函数指针要求您指定要传递给函数的参数(在本例中为 non(,并在表达式周围指定一组额外的括号,这些括号的计算结果将计算为要调用的函数(参数列表之前的所有内容(。
template<class Container, class MemberPtr>
class virtual_vector
{
public:
virtual_vector(const Container & p_container, MemberPtr p_member_ptr) :
m_container(&p_container),
m_member(p_member_ptr)
{}
// For mapping to a method
template<class T = MemberPtr>
auto operator[](std::enable_if_t<std::is_member_function_pointer<T>::value == true, const size_t> p_index) const
{
return ((*m_container)[p_index].*m_member)();
}
// For mapping to a member
template<class T = MemberPtr>
auto operator[](std::enable_if_t<std::is_member_function_pointer<T>::value == false, const size_t> p_index) const
{
return (*m_container)[p_index].*m_member;
}
private:
const Container * m_container;
MemberPtr m_member;
};
为了测试这一点,为了说明目的,我在Elem
类中添加了一个 getter。
struct Elem {
int a, b;
int foo() const { return a; }
Elem(int a, int b) : a(a), b(b) {}
};
这是它的使用方式:
int main() {
std::vector<Elem> printme = { Elem(1,100), Elem(2,200), Elem(3,300) };
std::vector<int> order = { 2,0,1 };
{ // print member
auto X = make_virtual_vector(printme, &Elem::a);
print_in_order(order, X);
}
{ // print method
auto X = make_virtual_vector(printme, &Elem::foo);
print_in_order(order, X);
}
}
您可以选择两种数据结构
struct Employee
{
std::string name;
double salary;
long payrollid;
};
std::vector<Employee> employees;
或者或者
struct Employees
{
std::vector<std::string> names;
std::vector<double> salaries;
std::vector<long> payrollids;
};
C++设计为第一个选项作为默认值。其他语言,如Javascript倾向于鼓励第二种选择。
如果您想找到平均工资,选项 2 更方便。如果要按薪水对员工进行排序,则选项 1 更易于使用。
但是,您可以使用 lamda 在两者之间进行部分互转换。lambda 是一个微不足道的小函数,它接受一个 Employee 并为他返回薪水 - 因此有效地提供了一个双精度的平面向量,我们可以取平均值 - 或者采用索引和一个 Employees 并返回一个员工,做一点琐碎的数据重新格式化。
template<class F>
struct index_fake_t{
F f;
decltype(auto) operator[](std::size_t i)const{
return f(i);
}
};
template<class F>
index_fake_t<F> index_fake( F f ){
return{std::move(f)};
}
template<class F>
auto reindexer(F f){
return [f=std::move(f)](auto&& v)mutable{
return index_fake([f=std::move(f),&v](auto i)->decltype(auto){
return v[f(i)];
});
};
}
template<class F>
auto indexer_mapper(F f){
return [f=std::move(f)](auto&& v)mutable{
return index_fake([f=std::move(f),&v](auto i)->decltype(auto){
return f(v[i]);
});
};
}
现在,按顺序打印可以重写为:
template <typename vectorlike>
void print(vectorlike const & printme) {
for (auto&& x:printme)
std::cout << x << std::endl;
}
template <typename vectorlike>
void print_in_order(std::vector<int> const& reorder, vectorlike const & printme) {
print(reindexer([&](auto i){return reorder[i];})(printme));
}
并将.a
打印为:
print_in_order( reorder, indexer_mapper([](auto&&x){return x.a;})(printme) );
可能有一些错别字。
- 将结构向量排序为子组
- NLOHMANN 的 JSON 库将数组转换为结构向量
- 如何在 c++ 中创建结构向量的映射
- 带unique_ptr的结构向量
- C++ - 如何在结构向量中找到结构体一个成员的最大值?
- 将信息输入到下面显示的结构向量中的正确语法/格式是什么
- C++对结构向量进行排序
- 在C++中对结构向量进行排序时出现问题
- 将结构向量保存到文件中,并从C++文件中读取结构向量
- 搜索并替换结构向量中的值
- 在结构向量中查找最接近的值
- 清除结构向量时会发生什么?
- 如何从 node-ffi 调用 c++ 中以结构向量作为参数的方法?
- 对结构向量进行两次排序
- 将一种数据类型的向量复制到同一数据类型的结构向量中的有效方法是什么
- 如何二进制搜索结构向量并在适当的索引处插入
- 结构向量中自定义结构函数的内存使用
- C++11-在结构向量中修改结构的成员
- 我正在尝试找到一个特定的结构成员,该成员是使用 lambda 函数的结构向量
- 在结构向量中搜索一个数据成员,然后打印匹配的所有数据成员