c ++ использует член класса в качестве параметра шаблона - PullRequest
0 голосов
/ 29 мая 2018

Я хочу иметь шаблонный класс с 2 параметрами шаблона: класс-контейнер и указатель на член, указывающий на массив значений.

У меня есть следующий пример:

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);
}

Все работает нормально, пока я не использую массивы фиксированного размера.

Что я должен исправить, чтобы этот пример работал?Самая важная часть для меня - иметь пример работы с Bar1 и Bar2.

Ответы [ 2 ]

0 голосов
/ 29 мая 2018

Проблема заключается в несовпадении указателей:

Foo<Bar1, &Bar1::y>
          ^

При взятии адреса Bar1::y получается указатель следующего типа:

int(Bar1::*)[42];

, т.е. указатель намассив размером 42, а не указатель на int.Приведение указателя работает:

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

, однако value (тип int) и container->*SomeMember (указатель типа на int) по-прежнему несовместимы, поэтому вам нужно либо изменить тип valueили разыменуйте указатель:

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

Конечно, reinterpret_cast s всегда вонючий, но при разыменовании указателя получается int, который является исходным типом рассматриваемых данных, поэтому на этот раз,приведение не должно нарушать строгие правила псевдонимов, и мы должны были избегать неопределенного поведения (см. также этот вопрос , допущен, помечен тегом C, но обычно C и C ++ совместимы в этом конкретном вопросе).

0 голосов
/ 29 мая 2018

Два возможных варианта - что-то вроде ниже, вероятно.

I.Добавьте дополнительный элемент к Bar1, который указывает на массив:

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

II.Сделайте Foo немного более универсальным:

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>;

До C ++ 17 вам нужно добавить третий параметр шаблона:

template<class, typename T, T member> struct Foo;
template<class Container, int *Container::*member> struct Foo<Container, int *Container::*, member>;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...