有没有办法访问结构的第 n 个元素?

时间:2020-12-25 04:38:27

标签: c++ struct types

我有一个指针结构,其中的指针是任意类型的,但在编译时是已知的。

struct ptrs
{
    int* a;
    const char** b;
    int* c;
    float* d;
};

鉴于它们都是指针,它们的长度都相同。有没有办法可以访问带有类型信息的第 n 个元素。

我可以将结构转换为空指针数组,但随后我将不知道我在看什么类型,同样使用联合。

整个代码库都在 c++20 中,所以我不需要担心 c 支持。

编辑:我想要这个用于以下用途。我将创建一个可变参数宏来将任意数量的输入转换为指向 then 的指针数组。这将传递给处理函数,该函数将根据命令字符串以任意顺序处理它们。例如。 “返回 $var1 + $var2”。处理需要随机访问元素一次或多次,因此“索引”一个结构。但是为了支持算术,需要知道类型。

2 个答案:

答案 0 :(得分:6)

是的,可以做到。有些代码编写起来有点棘手,但 Boost PFR 已经完成了繁重的工作,因此您可以执行以下操作:

ptrs p;

float * f = boost::pfr::get<3>(p); // get the `float *` member
*f = 123.4f;                       // and write via the pointer

[请注意,我只将其分成两行,以便我可以对每个步骤进行评论——像 *boost::pfr::get<3>(p) = 123.4f; 这样的内容也应该没问题。]

对于任何关心的人来说,你如何做到这一点的基本思想相当很简单——只是细节变得棘手。

基本方法是使用结构化绑定来获取您关心的项目。棘手的地方有几个:你不知道你在处理什么类型,而且类型各不相同,所以它不像创建一个项目数组并索引到数组那么简单。

但是,尽管简化了很多,我们可以做这样的事情:

template <std::size_t index>
constexpr auto& get(auto& your_struct)
{
    auto& [zero, one, two, three] = your_struct;

    if constexpr (index == 0)
        return zero;
    if constexpr (index == 1)
        return one;
    if constexpr (index == 2)
        return two;
    return three;
}

我再说一遍:我正在简化相当多的内容(例如,我刚刚使用了固定数量的成员),但这至少是大致的总体思路——使用结构化绑定来获取成员,并使用 constexpr条件来选择你关心的哪一个,基本上所有的部分都有模板参数(以 auto 的形式),这样你就可以处理碰巧在那里的任何类型。

哦,还有一个有点难以处理的细节:就目前而言,这种实现只能适用于一种类型。也就是说,如果您尝试使用 get<0>get<3>,您将收到关于推导返回类型不一致的错误。因此,就目前而言,您可以使用 get<0> get<2>(或其他),但不能同时使用,因此这纯粹是概念的有限证明,而不是实际实现(完全)。

不过,这是一个有效的演示程序:

#include <cstddef>
#include <iostream>

struct ptrs {
    int* a;
    const char** b;
    int* c;
    float* d;
};

template <std::size_t index>
constexpr auto& get(auto& your_struct)
{
    auto& [zero, one, two, three] = your_struct;

    if constexpr (index == 0)
        return zero;
    if constexpr (index == 1)
        return one;
    if constexpr (index == 2)
        return two;
    return three;
}

int main() {
    ptrs p;
    p.d = new float; // `get<3>(p) = new float;` works fine too.

    *get<3>(p) = 123.4;
    
    // show we're accessing the same data either way:
    std::cout << *get<3>(p) << "\t" << *(p.d) << "\n";

    // type information is retained. For example, if we try to do this:
    // get<3>(p) = new int;  // <-- note int instead of float
    // g++ 10 gives the error: "cannot convert ‘int*’ to ‘float*’ in assignment"
}

答案 1 :(得分:0)

这是唯一的方法,不用编造一堆元数据机制。

void* get(ptrs const& data, int n)
{
    // TODO: add checks
    return ((void**)&data)[n];
}

您应该注意,该标准并不能保证所有数据指针的大小都相同。但是,在大多数架构上,它们是。因此,请查阅您的编译器文档,或使用 static_assert 进行确认。

相关问题