4

I want to have a template class with 2 templates parameter: A Container class and a member pointer pointing to an array of values.

I've following Example:

template<class CONTAINER,int * CONTAINER::*SomeMember>
struct Foo {
    Foo(CONTAINER *container) {
        this->value = container->*SomeMember;
        this->container = container;
    }
    CONTAINER *container;
    int value;
};

struct Bar1 {
    char bla;
    int y[42];
};
struct Bar2 {
    int blablab;
    char bla;
    int y[42];
};
struct Bar3 {
    int * x;
};

void TEST() {
    Bar1 b1;
    Bar2 b2;
    Bar3 b3;

    //This one fails with
    // error: could not convert template argument '&Bar1::y' to 'int* Bar1::*'
    // error: invalid conversion from 'Bar1*' to 'int' [-fpermissive]
    Foo<Bar1,&Bar1::y> foo3(&b1);

    //This fails too
    Foo<Bar2,&Bar2::y> foo2(&b2);

    //This is working
    Foo<Bar3,&Bar3::x> foo(&b3);
}

The stuff is working fine as long as I don't use the fixed size arrays.

What does I have to correct to have this example working? The most important part for me is to have the example working with the Bar1 and Bar2.

powerpete
  • 2,663
  • 2
  • 23
  • 49
  • The assignment is incompatible anyway: `value = container->*SomeMember;`: value is of type int, but you assign a pointer to int to it. So you either need to change the type of `value` or additionally dereference: `value = *(container->*SomeMember);` – Aconcagua May 29 '18 at 13:39
  • You're going to be sad as long as you try to assign an array to a scalar. Maybe if you describe the problem you're trying to solve instead of asking why your solution isn;t working we could offer better assistance. – Stephen M. Webb May 29 '18 at 13:53

2 Answers2

1

Two possible options are something like below, probably.

I. Add an extra member to Bar1 that points to the array:

struct Bar1 {
    int y[42];
    int *z = y;
};
Foo<Bar1, &Bar1::z> foo1;

II. Make Foo slightly more generic:

template<class, auto> struct Foo;
template<class Container, int *Container::*member> struct Foo<Container, member>;
template<class Container, int (Container::&member)[42]> struct Foo<Container, member>;

Before C++17, you'll need to add a third template parameter:

template<class, typename T, T member> struct Foo;
template<class Container, int *Container::*member> struct Foo<Container, int *Container::*, member>;
bipll
  • 11,747
  • 1
  • 18
  • 32
  • Well, I've modified it after the first comment, apparently, shouldn't have done that. – bipll May 29 '18 at 13:18
  • Note that OP's system use non-`const` pointer data members, so it can be used to assign new addresses to the member pointers. If someone tried to assign a new address to `z` it will break your solution. – François Andrieux May 29 '18 at 13:19
  • Sure, but it's still better than nothing. Besides, for a value-changing template, it wouldn't do much good to apply it to a member array, anyway. – bipll May 29 '18 at 13:22
0

Problem is a mismatch of pointers:

Foo<Bar1, &Bar1::y>
          ^

Taking the address of Bar1::y results in a pointer of the following type:

int(Bar1::*)[42];

i. e. in a pointer to an array of size 42, not a pointer to int. Casting the pointer works:

Foo<Bar1, reinterpret_cast<int* Bar1::*>(&Bar1::y)> foo3(&b1);

however, value (type int) and container->*SomeMember (type pointer to int) are still not compatible, so you either need to change the type of value or dereference the pointer:

value = *(container->*SomeMember);
//      ^ (!)

Sure, reinterpret_casts are always smelly, but when dereferencing the pointer results in an int, which is the original type of the data in question, so this time, the cast should not break strict aliasing rules and we should have avoided undefined behaviour (see, too, this question, admitted, tagged C, but typically, C and C++ are compatible in this specific matter).

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
  • The issue isn't with strict-aliasing, but with pointer interconvertibility, and a pointer to member array isn't interconvertible to a pointer to its first element. I'm not certain, but since pointer to members are very scary beasts, it might realistically be a runtime error – Passer By May 29 '18 at 15:57
  • @PasserBy As long as the address is the same... Tried it with GCC and worked fine. – Aconcagua May 29 '18 at 16:13