是类的成员字段顺序“stable”? [英] Is the member field order of a class "stable"?
问题描述
考虑c ++(或c ++ 11),其中我有一些数据的数组,其中2 * N个整数表示N对。对于每个偶i = 0,2,4,6,...,2 * N,保持(data [i],data [i + 1])形成这样的对。现在我想有一个简单的方法来访问这些对,而不需要像下面这样写循环:
for(int i = 0; i <2 * N; i + = 2){... data [i] ... data [i + 1] ...}
所以我写了:
#include< iostream>
struct Pair {
int first; int second;
};
int main(){
int N = 5;
int data [10] = {1,2,4,5,7,8,10,11,13,14};
Pair * pairs =(Pair *)data;
for(int i = 0; istd :: cout< i<< :(<< pair [i] .first <,< pair [i] .second<<)< std :: endl;
return 0;
}
输出:
0:(1,2)
1:(4,5)
2:(7,8)
3:(10,11)
4:(13,14)
ideaone: http://ideone.com/DyWUA8
如您所见,我投放了int指针指向一个Pair指针,这样c ++只是处理我的数据是一个int的大小的两倍。我知道,因为这是数组的工作原理,数据数组是成对的两个sizeof(int)对齐。然而,我不知道我是否可以假设一对是正好两个sizeof(int)的成员字段第一和第二是按顺序(或对齐)存储。从技术上讲,在最糟糕的情况下,我可以想象编译器存储2个字节的第一,然后4的第二,然后2的第一(给定int的4个字节),并以某种方式管理这一点。当然,这可能是可笑的,但它是允许在c ++?
请注意,我不想将所有数据复制到新阵列,并手动将其转换为对。 Imho,这是一个昂贵的操作只是语法糖。
我可以假定对类的对齐?对结构同样有效吗?还有其他方法吗?
从我在这里读到的内容(这是否意味着我注定要复制我的数据或使用讨厌的语法?我可以以某种方式在c ++语言中强制最小对齐,或者我需要编译器开关吗?
解决方案
这是否意味着我注定要复制我的数据或使用讨厌的语法?
否
?
是的,使用包装类提供您喜欢的语法。以下是一种方法
#include< iostream>
struct Pairs {
int * _data;
Pairs(int data []):_data(data){}
int& first(size_t x)const {return _data [x * 2]; }
int& second(size_t x)const {return _data [x * 2 + 1]; }
};
int main(){
int N = 5;
int data [10] = {1,2,4,5,7,8,10,11,13,14};
对(数据);
for(int i = 0; istd :: cout< i<< :(<< pairs.first(i)<,<< pairs.second(i)<)< std :: endl;
return 0;
}
/ strong>
这里有一个解决方案,在一个结构体(如C ++ 11 std :: array)中包装一个int [2],但容忍由编译器后的int [2]。编译器不可能添加任何附加的填充,但标准不排除它。我还添加了一个随机访问迭代器,允许传递迭代器到std ::排序和排序原始数据作为对。
// http://stackoverflow.com/questions/23480041/is- -a-member-field-order-of-a-class-stable
#include< iostream>
#include< algorithm>
#include< stddef.h>
struct Pair {
int _data [2]; // _data [0]和_data [1]是连续的,
//和_data [0]在偏移0(& Pair ==& _data [0])
int _unused [6] ]; //模拟编译器在这里插入一些padding
int first(){return _data [0]; }
int second(){return _data [1]; }
int& operator [](size_t x){return _data [x]; }
friend inline bool operator< (const Pair& lhs,const Pair& rhs){
return lhs._data [0] rhs._data [0];
}
//编译器不可能向struct添加任何填充
// Pair,sizeof(Pair)== sizeof(_data)
//但是,标准不排除它,所以我们定义我们自己的
//复制构造函数和赋值运算符以确保没有
// extraneous存储
Pair(const Pair& other){
_data [0] = other._data [0];
_data [1] = other._data [1];
}
const Pair& operator =(const Pair& other){
_data [0] = other._data [0];
_data [1] = other._data [1];
return * this;
}
};
struct Pairs {
int * _data;
size_t _size;
对(int data [],size_t size):_data(data),_size(size){}
Pair& operator [](size_t x)const {
return * reinterpret_cast< Pair *>(_data + 2 * x);
}
class rai
:public std :: iterator< std :: random_access_iterator_tag,Pair>
{
int * _p;
size_t _size;
size_t _x;
public:
rai():_p(NULL),_size(0),_x(0){}
rai(int * data,size_t size)
:数据),_size(size),_x(0){}
friend inline bool operator ==(const rai& lhs,const rai& rhs){
return lhs._p == rhs._p& ;& lhs._x == rhs._x;
}
friend inline bool operator!=(const rai& lhs,const rai& rhs){
return lhs._p!= rhs._p || lhs._x!= rhs._x;
}
对& operator *()const {
return * reinterpret_cast< Pair *>(_p + 2 * _x);
}
rai& operator + =(ptrdiff_t n){
_x + = n;
if(_x> = _size){_x = _size = 0; _p = NULL; }
return * this;
}
rai&运算符 - =(ptrdiff_t n){
if(n> _x)_x = 0;
else _x - = n;
return * this;
}
friend inline rai operator +(rai lhs,const ptrdiff_t n){
return lhs + = n;
}
friend inline rai operator-(rai lhs,const ptrdiff_t n){
return lhs - = n;
}
friend inline bool operator< (const rai& lhs,const rai& rhs){
return * lhs< * rhs;
}
rai& operator ++(){return * this + = 1; } b $ b rai& operator--(){return * this - = 1; }
friend inline ptrdiff_t operator-(const rai& lhs,const rai& rhs){
return lhs._p == NULL
? (rhs._p == NULL?0:rhs._size - rhs._x)
:lhs._x - rhs._x;
}
};
inline rai begin(){return rai(_data,_size); }
static inline const rai end(){return rai(); }
};
int main(){
int N = 5;
int data [10] = {1,2,7,8,13,14,10,11,4,5};
对(数据,N);
std :: cout<< unsorted<< std :: endl;
for(int i = 0; istd :: cout< i<< :(<< pair [i] .first()<,
<< pairs [i] .second()< std :: endl;
std :: sort(pairs.begin(),pairs.end());
std :: cout<< sorted<< std :: endl;
for(int i = 0; istd :: cout< i
<< :(<< pair [i] [0] //与pairs [i]相同.first()
< / same as pairs [i] .second()
<<)< std :: endl;
return 0;
}
Considering c++ (or c++11), where I have some array of data with 2*N integers which represent N pairs. For each even i=0,2,4,6,...,2*N it holds that (data[i],data[i+1]) forms such a pair. Now I want to have a simple way to access these pairs without the need to write loops like:
for(int i=0; i<2*N; i+=2) { ... data[i] ... data[i+1] ... }
So I wrote this:
#include <iostream> struct Pair { int first; int second; }; int main() { int N=5; int data[10]= {1,2,4,5,7,8,10,11,13,14}; Pair *pairs = (Pair *)data; for(int i=0; i<N; ++i) std::cout << i << ": (" << pairs[i].first << ", " << pairs[i].second << ")" << std::endl; return 0; }
Output:
0: (1, 2) 1: (4, 5) 2: (7, 8) 3: (10, 11) 4: (13, 14)
ideaone: http://ideone.com/DyWUA8
As you can see, I cast the int pointer to a Pair pointer, such that c++ simply handles that my data is twice the size of an int. And I know, because that is how arrays work, that the data array is aligned in pairs of two sizeof(int)'s. However, I am not sure whether I can assume that a Pair is exactly two sizeof(int)'s and whether the member fields first and second are stored in that order (or alignment). Technically, in a worst case setting I can imagine that compiler stores 2 bytes for first, then 4 of second and then 2 of first (given that int's are 4 bytes), and somehow manages this. Of course, that is probably ludicrous, but is it allowed in c++?
Please note, I don't want to copy all the data to a new array and manually convert it to Pairs. Imho, that's an expensive operation for just syntax sugar.
May I assume the alignment of the Pair class? Does the same hold for structs? Are there other ways?
From what I read here ( How is the size of a C++ class determined? ) it is up to the compiler, and not in the language, of c++ how classes are aligned in memory. Does this mean that I am doomed to copy my data or use nasty syntax? Can I somehow force minimal alignment in the c++ language, or would I need compiler switches?
解决方案Does this mean that I am doomed to copy my data or use nasty syntax?
No
Are there other ways?
Yes, use a wrapper class that provides the syntax you like. Here's one way
#include <iostream> struct Pairs { int* _data; Pairs( int data[] ) : _data(data) {} int & first( size_t x ) const { return _data[x*2]; } int & second( size_t x ) const { return _data[x*2+1]; } }; int main() { int N=5; int data[10]= {1,2,4,5,7,8,10,11,13,14}; Pairs pairs( data ); for(int i=0; i<N; ++i) std::cout << i << ": (" << pairs.first(i) << ", " << pairs.second(i) << ")" << std::endl; return 0; }
Update
Here's a solution that wraps an int[2] in a struct (like C++11 std::array) but tolerates (in fact forces) padding by the compiler after the int[2]. The compiler is not likely to add any additional padding, but the standard doesn't preclude it. I've also added a random access iterator to allow passing iterators to std::sort and sort the original data as pairs. I did this for my one education, might be more trouble than it's worth.
// http://stackoverflow.com/questions/23480041/is-the-member-field-order-of-a-class-stable #include <iostream> #include <algorithm> #include <stddef.h> struct Pair { int _data[2]; // _data[0] and _data[1] are consecutive, // and _data[0] is at offset 0 (&Pair == &_data[0]) int _unused[6]; // simulate the compiler inserted some padding here int first() { return _data[0]; } int second() { return _data[1]; } int & operator[] ( size_t x ) { return _data[x]; } friend inline bool operator< ( const Pair & lhs, const Pair & rhs ) { return lhs._data[0] < rhs._data[0]; } // it is unlikely that the compiler will add any padding to a struct // Pair, so sizeof(Pair) == sizeof(_data) // however, the standard doesn't preclude it, so we define our own // copy constructor and assignment operator to ensure that nothing // extraneous is stored Pair( const Pair& other ) { _data[0] = other._data[0]; _data[1] = other._data[1]; } const Pair& operator=( const Pair& other ) { _data[0] = other._data[0]; _data[1] = other._data[1]; return *this; } }; struct Pairs { int* _data; size_t _size; Pairs( int data[], size_t size ) : _data(data), _size(size) {} Pair & operator[] ( size_t x ) const { return *reinterpret_cast< Pair * >( _data + 2 * x ); } class rai : public std::iterator<std::random_access_iterator_tag, Pair> { int * _p; size_t _size; size_t _x; public: rai() : _p(NULL), _size(0), _x(0) {} rai( int* data, size_t size ) : _p(data), _size(size), _x(0) {} friend inline bool operator== (const rai& lhs, const rai& rhs) { return lhs._p == rhs._p && lhs._x == rhs._x; } friend inline bool operator!= (const rai& lhs, const rai& rhs) { return lhs._p != rhs._p || lhs._x != rhs._x; } Pair& operator* () const { return *reinterpret_cast< Pair * >( _p + 2 * _x ); } rai& operator+=( ptrdiff_t n ) { _x += n; if (_x >= _size) { _x = _size = 0; _p = NULL; } return *this; } rai& operator-=( ptrdiff_t n ) { if (n > _x) _x = 0; else _x -= n; return *this; } friend inline rai operator+ ( rai lhs, const ptrdiff_t n ) { return lhs += n; } friend inline rai operator- ( rai lhs, const ptrdiff_t n ) { return lhs -= n; } friend inline bool operator< ( const rai & lhs, const rai & rhs ) { return *lhs < *rhs; } rai& operator++ () { return *this += 1; } rai& operator-- () { return *this -= 1; } friend inline ptrdiff_t operator-(const rai& lhs, const rai& rhs) { return lhs._p == NULL ? (rhs._p == NULL ? 0 : rhs._size - rhs._x) : lhs._x - rhs._x; } }; inline rai begin() { return rai(_data,_size); } static inline const rai end() { return rai(); } }; int main() { int N=5; int data[10]= {1,2,7,8,13,14,10,11,4,5}; Pairs pairs( data, N ); std::cout << "unsorted" << std::endl; for(int i=0; i<N; ++i) std::cout << i << ": (" << pairs[i].first() << ", " << pairs[i].second() << ")" << std::endl; std::sort(pairs.begin(), pairs.end()); std::cout << "sorted" << std::endl; for(int i=0; i<N; ++i) std::cout << i << ": (" << pairs[i][0] // same as pairs[i].first() << ", " << pairs[i][1] // same as pairs[i].second() << ")" << std::endl; return 0; }
这篇关于是类的成员字段顺序“stable”?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!