Reputation: 3052
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.
Upvotes: 4
Views: 2773
Reputation: 25546
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_cast
s 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).
Upvotes: 0
Reputation: 11950
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>;
Upvotes: 1