对数组中特定位置的调试器友好零成本命名引用
Debugger-friendly zero-cost named references to specific locations in an array
struct A
{
int a = 1;
short b = 2;
char c = 3;
}
struct B
{
using arr_type = array<A,3>;
char asd = 0;
A a1;
A a2;
A a3;
// is this safe to use to loop trough all 3 elements?
arr_type* p1 = reinterpret_cast<arr_type*>(&a1);
// or maybe this one?
A* p2 = &a1;
};
我可以安全地使用p1
或p2
从a1...a3
循环吗?
B b;
for (int i = 0; i < 3; i++)
{
cout << p1[i];
cout << p2[i];
}
它不是一个简单的数组的原因是我希望每个"项目"都有一个正确的名称。
我可以改用联合方法,但C++禁止匿名结构(尽管这对我来说不是问题,因为 MSVC 支持这一点,GCC 似乎也支持它(;
union E
{
A arr[3];
struct {
A a1;
A a2;
A a3;
};
};
以下内容显然是安全的,但每个引用都有 4 个字节的开销。我不喜欢。(加上初始化引用的成本。
struct B
{
char asd;
A arr[3];
A& a1 = arr[0];
A& a2 = arr[1];
A& a3 = arr[2];
};
这个没有开销,但对于我非常具体的情况来说,这还不够好。
struct B
{
char asd;
A arr[3];
A& a1() { return arr[0] };
A& a2() { return arr[1] };
A& a3() { return arr[2] };
};
我将经常使用这些a1, a2, a3
名称,如果它们是Visual Studio中的函数调用,则很难调试它们。同样,我将大量使用这些字段,因此我希望能够轻松检查它们的值。
struct B { using arr_type = array<A,3>; char asd = 0; A a1; A a2; A a3; // is this safe to use to loop trough all 3 elements? arr_type* p1 = reinterpret_cast<arr_type*>(&a1); };
结构需要根据它们的类型自然对齐,数组也是如此,但我不知道有任何规则说它们必须是相同的对齐点。
如果有这样的规则,像这样的成员的结构布局边界和数组边界将是相同的 - 它仅适用于标准布局结构:
https://stackoverflow.com/a/7189821/211160
如果您执行以下操作,则所有赌注都将关闭:
private:
A a1;
A a2;
public:
A a3;
我想如果它包含任何关闭标准布局开关的东西,所有的赌注都会失败。 由于一开始就值得怀疑,我想说当时甚至不要这样做。
(我也想知道数组与结构#pragma pack()
会带来什么样的差异......并不是说 #pragmas 在标准中,我只是想知道。
union E { A arr[3]; struct { A a1; A a2; A a3; }; };
不,arr[N]
和aN
不会等同。 关于如何在联合中使用初始序列以兼容C++读取,有一些微妙的细节......但这仅在具有兼容序列的结构之间。 它没有说明结构和数组:
键入 C 中的结构,并通过联合C++
我将经常使用这些 a1、a2、a3 名称,如果它们是 Visual Studio 中的函数调用,则很难调试它们。同样,我将大量使用这些字段,因此我希望能够轻松检查它们的值。
"以下内容显然是安全的,但每个引用都有 4 个字节的开销">
在实践中,您似乎是对的,今天的 GCC 并没有对其进行优化(根据您的链接(:
https://godbolt.org/g/6jAtD5
http://ideone.com/zZqfor
这令人失望,它们可以被优化出来,因为标准中没有任何内容说它们必须占用空间。 它们在内部指向结构,并且在结构的生命周期内不会更改。 :-/
您对将被优化的函数访问的抱怨是它对调试器不够友好。 为什么不两者兼而有之呢?
struct B
{
char asd;
A arr[3];
A& a1() { return arr[0] }
const A& a1() const { return arr[0]; }
A& a2() { return arr[1] };
const A& a2() const { return arr[1]; }
A& a3() { return arr[2] };
const A& a3() const { return arr[2]; }
#if !defined(NDEBUG)
A& a1_debug = arr[0];
A& a2_debug = arr[1];
A& a3_debug = arr[2];
#endif
};
<小时 />如果投影数据结构的调试器友好性功能对您很重要......学习如何为您的环境编写自定义调试器帮助程序可能会很好地利用时间,例如:
http://doc.qt.io/qtcreator/creator-debugging-helpers.html
我想这是否值得取决于你多久有这种担忧。
不需要这么肮脏。
std::tuple
与 lambda 相结合,为您提供所需的所有功能。此外,它是完全合法、最佳和正确的。
如果我们定义一个成员函数,该函数返回对结构中所有 As 的引用元组:
auto all_a() const {
return std::tie(a1, a2, a3);
}
。然后创建一个小管道,以提供一种在元组上行走的方法(见下文(......
。我们可以编写这样的代码:
B b;
for_each(b.all_a(),
[](const A& a) { std::cout << a << std::endl; });
完整示例(虽然我没有实现运算符<<,但您可以自己执行此操作(。
#include<iostream>
#include<array>
#include<tuple>
#include<utility>
using namespace std;
struct A
{
int a = 1;
short b = 2;
char c = 3;
};
std::ostream& operator<<(std::ostream& os, const A& a);
struct B
{
char asd = 0;
A a1;
A a2;
A a3;
auto all_a() const {
return std::tie(a1, a2, a3);
}
};
template<class Tuple, size_t...Is, class F>
void for_each_impl(const Tuple& t, std::index_sequence<Is...>, F&& f)
{
using expand = int[];
void(expand { 0, (void(f(std::get<Is>(t))),0)... });
}
template<class...Ts, class F>
void for_each(const std::tuple<Ts...> ts, F&& f)
{
using expand = int[];
for_each_impl(ts,
std::make_index_sequence<sizeof...(Ts)>(),
std::forward<F>(f));
}
int main()
{
B b;
for_each(b.all_a(),
[](const A& a) { std::cout << a << std::endl; });
}
- 当回溯以零开始时,如何调试崩溃
- 将对象数组的引用传递给函数
- 什么时候在C++中返回常量引用是个好主意
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- 在没有太多条件句的情况下,我如何避免被零除
- 何时在引用或唯一指针上使用移动语义
- 如何在c++中使用引用实现类似python的行为
- OpenInventor从9.8升级到10.4.2后,GLSL纹理返回零
- 编译C++时未定义的引用
- Ctypes wstring通过引用传递
- c++r值引用应用于函数指针
- 理解c++中的引用
- 如果我们通过引用传递变量,则递归中使用的堆栈空间量是否为零?
- 引用计数为零,仍然没有分段错误
- 将一包转发引用包装成元组
- 对数组中特定位置的调试器友好零成本命名引用
- 给出对类的零大小向量的引用不起作用
- 为什么指针在引用零值时为空
- 在 Xcode 6.3 下,NULL C++引用地址的计算结果为非零
- 引用计数永远不会减少到零