【问题标题】:Is there a way to loop over different data members of an object in C++有没有办法在 C++ 中循环对象的不同数据成员
【发布时间】:2020-06-11 15:33:48
【问题描述】:

如果我有一个由 3 个双精度组成的类对象; x、y 和 z,有没有办法在函数中循环它们?

例如

for(i in (x,y,z))
    do something;

我可以通过显式转换来做类似的事情,但我想知道是否有更优雅的解决方案。

class testc {
public:
    double x, y, z;

    testc(double x, double y, double z)
        :x(x), y(y), z(z)
    {}
};

int main()
{
    testc omega(1, 2, 3);
    cout << *(double*)&omega << " " << *((double*)&omega +1) << " " << *((double*)&omega +2);
}

【问题讨论】:

  • 你的成员总是有相同的类型吗?
  • 认为 *((double*)&amp;omega +1)(你忘了括号)是一个严格的别名违规和UB,它需要是*(double *)((char*)&amp;omega + sizeof(double))
  • 从技术上讲,您可以拥有for (auto el : { omega.x, omega.y, omega.z }) {// your code}。使用向量或数组容器可能会更好。
  • 抱歉看到这个关闭了,我只是使用基于范围的循环初始化语句 (C++20) 编写答案,允许我们使用结构化绑定作为 init 语句:for(auto [x, y, z] = omega; auto e : {x, y, z}) { std::cout &lt;&lt; e &lt;&lt; " "; } (给予或接受一些auto constness)。
  • @dfri:我重新打开了它。我的回答很弱。放置一个占位符以阻止欺骗警察!

标签: c++


【解决方案1】:

(C++20) 对已知数量的公共成员进行循环:基于范围的 for 循环初始化语句中的结构化绑定

从 C++20 开始,我们可以将 structured bindingsrange-based for loop 初始化语句结合起来(后者是 C++20 的一个特性):

语法

for ( init-statement(optional) range-declaration : range-expression ) ...

具体来说,在基于范围的 for 循环中使用结构化绑定作为 init 语句

#include <iostream>

class Foo {
public:
    double x, y, z;

    Foo(double x, double y, double z) : x(x), y(y), z(z) {}
};

int main() {
    const Foo foo(1., 2., 3.);

    for (auto [x, y, z] = foo; auto e : {x, y, z}) {
        std::cout << e << " ";
    } // 1 2 3

    return 0;
}

但是请注意,您只能使用结构化绑定来分解类的公共成员(在您的示例中,所有成员都是公共的)。此外,对于基于范围的 for 循环的 range-expression 中的初始化程序列表,您可能没有冲突的类型,这意味着这种方法仅限于您的示例的上下文:所有公共成员都属于同一类型。

为什么要使用结构化绑定而不是直接对类成员进行列表初始化?

现在,如果您的类的(公共)成员以及它的实例都有非常简短的名称,我们可能需要考虑省略结构化绑定,而是直接对类成员进行列表初始化:

const Foo f(1., 2., 3.);
for (auto e : {f.x, f.y, f.z}) {
    std::cout << e << " ";
} // 1 2 3

然而,尽管可以说更简短,但缺点是我们不再从编译器那里得到任何帮助来发现我们是否真的准确地分解了所有公共成员(不少于,不多),当检查时出现我们使用结构化绑定:

for (auto [x, y] = foo; auto e : {x, y}) { /* ... */ }
// error: type 'Foo' decomposes into 3 elements, 
//        but only 2 names were provided

for (auto [x, y, z, xx] = foo; auto e : {x, y, z, xx}) { /* ... */ }
// error: type 'Foo' decomposes into 3 elements, 
//        but 4 names were provided

【讨论】:

  • 这很豪华。 omega 哪里来的?
  • @Bathsheba 抱歉,我只是参考了 OP:s 自己的示例,但最好是独立的,谢谢。
  • 由于这个问题被暂时关闭为重复,我在这里提出了相同的解决方案:stackoverflow.com/questions/19059157/…。但是,如果这种方法可以扩展到任意数量的元素,我很感兴趣。
【解决方案2】:
for (auto&& i : std::vector<double>{x, y, z}){
    // Do something, `i` is the double.
}

是一种方式,代价是创建向量。我不相信编译器会将此检测为惯用语并进行优化。

*(&amp;x + 1) 这样的代码试图访问y 的行为是未定义的。 (指针算法只在数组中有效。)

您不能对xyz 使用std::arraystd::vector 吗?

【讨论】:

  • @Skcoica:未定义行为的一种表现是代码似乎可以工作。但它仍然是 UB,一个好的编译器会发出警告。
  • @Bathsheba *(double*)((char*)&amp;x + sizeof x) 是明确定义的,还是 UB?
  • @Skcoica 您可以查看文档以了解如何在编译期间打开更多(或任何?)警告。
  • @Skcoica:MSVC 非常宽松。鉴于 COM 的实现依赖于这些东西,MSVC 开发人员不太可能为这类东西添加警告。
  • 为什么使用std::vector 而不是(std::)initializer_listfor (auto&amp;&amp; i : {x, y, z})?
猜你喜欢
  • 2015-02-14
  • 1970-01-01
  • 2020-11-20
  • 1970-01-01
  • 1970-01-01
  • 2021-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多