Accessing struct members with array subscript operator

后端 未结 5 1721
萌比男神i
萌比男神i 2021-01-07 10:49

Let have a type T and a struct having ONLY uniform elements of T type.

struct Foo {
    T one,
    T two,
    T three
};

I\'d like to acces

相关标签:
5条回答
  • 2021-01-07 11:29

    Another way is with template specialization if what you are trying to achieve is still a compile time feature.

    class Foo {
        T one;
        T two;
        T three; 
    };
    
    template <int i> T & get(Foo& foo);
    
    template T& get<1>(Foo& foo){ return foo.one;}
    template T& get<2>(Foo& foo){ return foo.two;}
    template T& get<3>(Foo& foo){ return foo.three;}
    

    It would be nice to define get as a member function but you cannot specialize template member functions. Now if this is only a compile time expansion you are looking for then this will avoid the lookup table issue of one of the previous posts. If you need runtime resolution then you need a lookup table obviously.

    -- Brad Phelan http://xtargets.heroku.com

    0 讨论(0)
  • 2021-01-07 11:34

    You can't because the compiler can add dead bytes between members to allow padding.

    There is two ways to do what you want.

    The first is to use your compiler-specific keyword or pragma macro that will force the compiler to not add padding bytes. But that is not portable. That said it might be the easiest way to do it with your macro requirements, so I suggest you explore this possibility and prepare for adding more pragma when using different compilers.

    The other way is to first make sure your members are aligned, then add accessors :

    struct Foo {
    
       T members[ 3 ]; // arrays are guarrantied to be contigu
    
    
       T& one() { return members[0]; } 
       const T& one() const { return members[0]; } 
       //etc... 
    
    };
    
    0 讨论(0)
  • 2021-01-07 11:36

    You might be able to achieve what you want using an array to hold the data (so you can get indexed access without using a lookup table) and having references to the various array elements (so you can have 'named' elements for use by your macros).

    I'm not sure what your macros require, so I'm not 100% sure this will work, but it might. Also, I'm not sure that the slight overhead of the lookup table approach is worth jumping through too many hoops to avoid. On the other hand, I don't think the approach I suggest here is any more complex than the table-of-pointers approach, so here it is for your consideration:

    #include <stdio.h>
    
    template< typename T >
    struct Foo {
    
    private:    
        T data_[3];
    
    public:
    
        T& one;
        T& two;
        T& three;
    
        const T& operator[](size_t i) const {
            return data_[i];
        }
    
        T& operator[](size_t i) {
            return data_[i];
        }
    
        Foo() :
            one( data_[0]),
            two( data_[1]),
            three( data_[2])
            {};
    
    };
    
    
    int main()
    {
        Foo<int> foo;
    
        foo[0] = 11;
        foo[1] = 22;
        foo[2] = 33;
    
        printf( "%d, %d, %d\n", foo.one, foo.two, foo.three);
    
        Foo<int> const cfoo( foo);
    
        printf( "%d, %d, %d\n", cfoo[0], cfoo[1], cfoo[2]);
    
        return 0;
    }
    
    0 讨论(0)
  • 2021-01-07 11:40

    If you're sure the compilers you're using are going to generate the right code for this (and I'd imagine they would, assuming T isn't a reference type anyway) the best thing to do is put in some kind of check that the struct is laid out as you think. I can't think of any particular reason to insert non-uniform padding between adjacent members of the same type, but if you check the struct layout by hand then you'll at least know if it happens.

    If the struct (S) has exactly N members of type T, for example, you can check at compile time that they are tightly packed simply using sizeof:

    struct S {
        T a,b,c;
    };
    
    extern const char check_S_size[sizeof(S)==3*sizeof(T)?1:-1];
    

    If this compiles, then they're tightly packed, as there's no space for anything else.

    If you just happen to have N members, that you want to ensure are placed directly one after the other, you can do something similar using offsetof:

    class S {
        char x;
        T a,b,c;
    };
    
    extern const char check_b_offset[offsetof(S,b)==offsetof(S,a)+sizeof(T)?1:-1];
    extern const char check_c_offset[offsetof(S,c)==offsetof(S,b)+sizeof(T)?1:-1];
    

    Depending on the compiler, this might have to become a runtime check, possibly not using offsetof -- which you might want to do for non-POD types anyway, because offsetof isn't defined for them.

    S tmp;
    assert(&tmp.b==&tmp.a+1);
    assert(&tmp.c==&tmp.b+1);
    

    This doesn't say anything about what to do if the asserts start failing, but you should at least get some warning that the assumptions aren't true...

    (By the way, insert appropriate casts to char references and so on where appropriate. I left them out for brevity.)

    0 讨论(0)
  • 2021-01-07 11:44

    Your code doesn't work with NON-POD types such those which using virtual member functions. There is a standard compliant (and efficient) way to achieve what you're trying to do, using pointer to data members:

    template< typename T >
    struct Foo {
    
        typedef size_t size_type;
    
    private:
    
        typedef T Foo<T>::* const vec[3];
    
        static const vec v;
    
    public:
    
        T one;
        T two;
        T three;
    
        const T& operator[](size_type i) const {
            return this->*v[i];
        }
    
        T& operator[](size_type i) {
            return this->*v[i];
        }
    };
    
    template< typename T >
    const typename Foo<T>::vec Foo<T>::v = { &Foo<T>::one, &Foo<T>::two, &Foo<T>::three };
    

    Just make sure you use const every with the table of pointer to data-members to get optimizations. Check here to see what I'm talking about.

    0 讨论(0)
提交回复
热议问题