将结构向量实际上转换为结构成员向量 [英] Virtually turn vector of struct into vector of struct members

查看:40
本文介绍了将结构向量实际上转换为结构成员向量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个接受类似向量的输入的函数.为了简化事情,让我们使用这个 print_in_order 函数:

I have a function that takes a vector-like input. To simplify things, let's use this print_in_order function:

#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.a.我可以通过创建一个新的 vector<int>(复制 a 为所有 Elems)并将其传递给打印函数来做到这一点 - 但是,我觉得必须有一个传递虚拟"向量的方法,当在其上使用 operator[] 时,仅返回成员 a.请注意,我不想更改 print_in_order 函数来访问成员,它应该保持通用.

Now I have a vector<Elem> and want to print a single integer member, Elem.a, for each Elem in the vector. I could do this by creating a new vector<int> (copying a for all Elems) and pass this to the print function - however, I feel like there must be a way to pass a "virtual" vector that, when operator[] is used on it, returns this only the member a. Note that I don't want to change the print_in_order function to access the member, it should remain general.

这可能吗,也许使用 lambda 表达式?完整代码如下.

Is this possible, maybe with a lambda expression? Full code below.

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

推荐答案

使用成员指针,您可以实现一种代理类型,该类型允许您通过将每个对象替换为其成员之一来查看对象容器(请参阅 指向数据成员的指针)或通过它的一个 getter(参见 指向成员函数的指针).第一个解决方案只针对数据成员,第二个解决方案针对两者.

Using member pointers you can implement a proxy type that will allow you view a container of objects by substituting each object by one of it's members (see pointer to data member) or by one of it's getters (see pointer to member function). The first solution addresses only data members, the second accounts for both.

容器必须知道使用哪个容器以及映射哪个成员,这将在构造时提供.指向成员的指针的类型取决于该成员的类型,因此必须将其视为额外的模板参数.

The container will necessarily need to know which container to use and which member to map, which will be provided at construction. The type of a pointer to member depends on the type of that member so it will have to be considered as an additional template argument.

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[] 运算符,因为您提到它是您想要访问元素的方式.取消引用成员指针的语法起初可能令人惊讶.

Next, implement the operator[] operator, since you mentioned that it's how you wanted to access your elements. The syntax for dereferencing a member pointer can be surprising at first.

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

要使用这个实现,你可以这样写:

To use this implementation, you would write something like this :

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

这有点麻烦,因为没有发生模板参数推导.因此,让我们添加一个免费函数来推断模板参数.

This is a bit cumbersome since there is no template argument deduction happening. So lets add a free function to deduce the template arguments.

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_ifstd::is_member_function_pointer(均在 <一个 href="http://en.cppreference.com/w/cpp/header/type_traits" rel="nofollow noreferrer"> 标头),它允许我们这样做.成员函数指针要求您指定要传递给函数的参数(在本例中为非),并在将计算为要调用的函数的表达式周围附加一组括号(参数列表之前的所有内容).

If you want to support member functions, it's a little bit more complicated. First, the syntax to dereference a data member pointer is slightly different from calling a function member pointer. You have to implement two versions of the operator[] and enable the correct one based on the member pointer type. Luckily the standard provides std::enable_if and std::is_member_function_pointer (both in the <type_trait> header) which allow us to do just that. The member function pointer requires you to specify the arguments to pass to the function (non in this case) and an extra set of parentheses around the expression that would evaluate to the function to call (everything before the list of arguments).

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.

To test this, I've added a getter to the Elem class, for illustrative purposes.

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

这篇关于将结构向量实际上转换为结构成员向量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆