powerpete
powerpete

Reputation: 3052

c++ use a class member as a template parameter

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

Answers (2)

Aconcagua
Aconcagua

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_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).

Upvotes: 0

bipll
bipll

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

Related Questions