2015-11-13 3 views
3

У меня есть структура, которая содержит другие структуры, а также примитивные типы данных. Я хотел бы иметь доступ к каждому члену, включая членов структур, содержащихся в основной структуре, с помощью функции шаблона.Функция шаблона для доступа к элементам структуры

Например:

struct B { 
    int y; 
    int z; 
}; 

struct A { 
    int x; 
    B b; 
}; 

template <typename TMember> 
bool doSomething(A *a, TMember member) { 

    a->*member = 5; // some code accessing member 

} 

// Then access it with: 

doSomething(&myA, &A::x); 

// and likewise 

doSomething(&myA, &A::b.y); 

Однако второй не будет компилировать и бросить «объект отсутствует в ссылке на» ошибки. Я предполагаю, потому что у A нет члена b.y?

Есть ли какой-либо способ получить функциональность, которую я хочу, или будет кодировать другую функцию?

(Обратите внимание, это всего лишь пример, и код, который у меня есть содержит большую структуру и сэкономит мне больше времени, чем просто писать вторую функцию.)

Спасибо!

+2

Вы уверены, что первые компилирует? Конечно, вычет шаблона будет означать, что это int, но он все еще пытается получить доступ к нестационарному члену. – Aesthete

+0

Я не думаю, что вы сможете делать то, что хотите. Вам нужно будет написать еще одну функцию. Не зная, что вы делаете, или видите свою «структуру», моя реакция на это заключается в том, что дизайн неправильный и должен быть переосмыслен. – pstrjds

+0

«Я хотел бы иметь доступ к каждому члену, включая членов структур, содержащихся в основной структуре». Это было бы нарушением Закона Деметры. Не. –

ответ

1

простой способ добиться того, чтобы использовать указатели на функции в качестве параметров вашей функции шаблона:

// Example program 
#include <iostream> 
#include <string> 

struct B{ 
    int y; 
    int z; 
}; 

struct A{ 
    int x; 
    B b; 
}; 

int& get1(A& a) { return a.x; } 
int& get2(A& a) { return a.b.y; } 

template <typename T> 
bool doSomething(A *a, T& (*getter)(A&)){ 

    T& attr = (*getter)(*a); // get reference to the attribute 
    attr = 5; // modify the attribute 
    return true; 
} 


int main() 
{ 
    A myA; 

    doSomething(&myA, &get1); 
    doSomething(&myA, &get2); 
} 
+0

Это действительно отличный вариант для того, что мне нужно. Ура! –

2

Там есть пара вопросов.

Проще всего то, что A::x искажен: вам нужен указатель на член, и для этого требуется адрес-оператор. doSomething(&myA, &A::x) будет работать нормально.

Второй сложнее, поскольку на C++ нет способа сформировать указатель на член элемента. Если вы не хотите делать взломанные, небезопасные вещи с offsetof, вам понадобится нечто более мощное, чем указатель. Например, вы могли бы пройти в лямбда, который возвращает ссылку на члена:

template <typename TAccessor> 
void doSomething(A *a, TAccessor accessor){ 

    accessor(a) = 5; 
} 

int main() 
{ 
    A myA; 
    doSomething(&myA, [](A* a)->int&{ return a->b.y; }); 
} 

Очевидно, что оставляет желать лучшего, читаемость-мудрый.

EDIT: О, и если ваше возможное решение основано на указателях-членах, обратитесь за советом 101010 и сделайте параметр шаблона конкретным параметром шаблона-указателя-члена. Мало того, что это возможно более эффективно, это более самодокументируемо, а ошибки компилятора, если вы испортите, будут в десять раз более четкими.

+0

Имеет смысл. Я думаю, что для удобства чтения я просто напишу некоторые дополнительные функции. Спасибо за вашу помощь! –

0

В вашем примере вам не хватает & в описании ссылки. A :: b.y - это что-то нерешенное. A :: b :: y и A :: B :: y тоже плохие. Вероятно, вы можете использовать B :: y.

doSomething(&myA, &B::y); 

Но это не член A, и это может привести к неправильному поведению. Или нет. Поэтому следует заменить с

template <class TOwner, typename TMember> 
bool doSomething(TOwner * owner, TMember member); 

Кроме того, я не вижу никакой std::atomic в вашем коде. Вы имели в виду интегральные типы вместо атомных?

+0

Извините, я забыл & & да, я имел в виду интеграл. Обновлено мое сообщение сейчас Спасибо! –

1

Вы не можете сделать указатель на указатель на член (это forbidden by the language). Тем не менее, вы можете продолжать доступ к указателям членам до конца.

Если мы обобщим доступ с помощью рекурсивной функции, которая держит вызов указателей на член:

template <typename C, typename T> 
decltype(auto) access(C& cls, T C::*member) { 
    return (cls.*member); 
} 

template <typename C, typename T, typename... Mems> 
decltype(auto) access(C& cls, T C::*member, Mems... rest) { 
    return access((cls.*member), rest...); 
} 

Тогда можно записать doSomething взять произвольное количество указателей:

template <typename... Members> 
void doSomething(A *a, Members... mems) { 
    access(*a, mems...) = 5; 
} 

И назвать это:

A myA; 
doSomething(&myA, &A::x); 
doSomething(&myA, &A::b, &B::y); 
+0

Этот ответ нужно поддержать! – 101010

0

Вы можете делать то, что хотите для члена класса var iables передавая указатель на переменную члена, как показано ниже:

template <typename T> 
void doSomething(A *a, T A::*member){ 
    a->*member = 5; 
} 

LIVE DEMO