是类的成员字段顺序“stable”? [英] Is the member field order of a class "stable"?

查看:89
本文介绍了是类的成员字段顺序“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; i std :: 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 ++语言中强制最小对齐,或者我需要编译器开关吗?


这是否意味着我注定要复制我的数据或使用讨厌的语法?




是的,使用包装类提供您喜欢的语法。以下是一种方法



http://ideone.com/nitI0B

  #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; i std :: cout< i<< :(<< pairs.first(i)<,<< pairs.second(i)<)< std :: endl;

return 0;
}






/ strong>



这里有一个解决方案,在一个结构体(如C ++ 11 std :: array)中包装一个int [2],但容忍由编译器后的int [2]。编译器不可能添加任何附加的填充,但标准不排除它。我还添加了一个随机访问迭代器,允许传递迭代器到std ::排序和排序原始数据作为对。



在ideone中查看

  // 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; i std :: 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; i std :: 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

http://ideone.com/nitI0B

#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.

See this in ideone

// 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆