【问题标题】:Pointers to member arrays with differing sizes指向不同大小的成员数组的指针
【发布时间】:2019-10-04 16:02:49
【问题描述】:

假设我有一个包含两个数组成员的类,它们的元素类型相同但大小不同:

struct X
{
  string a[2];
  string b[3];
};

constexpr auto array_members = std::array{ &X::a, &X::b };

这不会编译,因为两个数组(具有不同的长度)具有不兼容的类型。

是否有一个共同的类型可以同时分配两个成员指针?

我也尝试过static_cast<string (X::*)[]>(&X::a),但由于数组类型不完整,这也无法编译。

我不能使用offsetof,因为它需要与非标准布局类一起使用。

这可能是一种解决方法:

using array_type = decltype(X::b);
auto const array_members = std::array{
  reinterpret_cast<array_type X::*>(&X::a), // cannot be constexpr
  &X::b
};

但我担心调用未定义的行为。尽管我确信在运行时没有越界元素引用,但我们确实创建了指向 a 的过去的指针,鉴于 b 的类型似乎是在界内.我不确定这是否违反规范。如果我可以使用与reinterpret_cast 不兼容的constexpr 也会很方便。

【问题讨论】:

  • 是的,它叫做联合。
  • std::vector 可让您绕过所有这些,因为大小不是类型的一部分。
  • @NathanOliver 这会丢失成员的编译时大小信息。
  • 可以选择用std::tuple 替换std::array

标签: c++ arrays c++17 pointer-to-member


【解决方案1】:

您不能拥有指向不同类型成员的成员指针数组。

您可以使用某种将 span 返回给成员的函数来代替成员指针:

template<auto m>
constexpr auto getter = [](X& x) noexcept
                        // X could be deduced from m for extra genericity
                        // https://stackoverflow.com/questions/25228958
{

    return span{x.*m, std::size(x.*m)};
};

constexpr auto array_members = std::array{ +getter<&X::a>, +getter<&X::b> };

No assembly generated :) 处于零优化级别(当然,直到您实际调用函数)。 array_members 是一个函数指针数组。示例用法:

X x;
span a = array_members[0](x);
a[0] = "test";

这里使用了span,C++17标准库中没有,所以你需要使用它的另一个实现。

【讨论】:

  • 编译时类型擦除...通过模板化的 lambda 完成。做得好。我希望我能投票两次
  • 谢谢 - + 对吸气剂有什么影响......为什么&amp; 没有同样的效果?
  • 好的,我找到了解释它的这个问题:stackoverflow.com/questions/17822131/…
【解决方案2】:

你可以使用std::variantstd::array来实现你想要的:

#include <variant>
#include <array>

struct X
{
    std::variant<array<string, 2>, array<string, 3>> a;
    std::variant<array<string, 2>, array<string, 3>> b;

};

 constexpr auto array_members = std::array{ &X::a, &X::b };
 array<string, 3> arr;
 std::variant<array<string, 2>, array<string, 3>> a = arr;
 cout<<"test array: "<<std::get<1>(a)[0];
 //cout<<std::get<0>(a)[0]; will throw 

缺点是ab 会占用与array&lt;string, 3&gt; 一样多的内存。 std::variant 是类型安全的。

直播godbolt

【讨论】:

    猜你喜欢
    • 2012-08-13
    • 2023-03-27
    • 2011-04-03
    • 1970-01-01
    • 1970-01-01
    • 2018-03-24
    • 1970-01-01
    • 2010-10-15
    相关资源
    最近更新 更多