То, что мне нужно, может быть сделано путем сохранения this
указателя включения класса в вложенный класс, например, следующим образом:
class CEnclosing {
public:
class CNested : public CSomeGeneric {
public:
CNested(CEnclosing* e) : m_e(e) {}
virtual void operator=(int i) { m_e->SomeMethod(i); }
CEnclosing* m_e;
};
CNested nested;
CEnclosing() : nested(this) {}
virtual void SomeMethod(int i);
};
int main()
{
CEnclosing e;
e.nested = 123;
return 0;
}
Это хорошо работает, но требует больше sizeof(void*)
байтов памяти для каждого вложенного класса-члена. Exist эффективный и переносимый способ сделать это без необходимости хранить указатель на экземпляр CEnclosing
в m_e
?
Как было сказано ранее, C++ не дает никакого способа сделать это. Вложенный класс не имеет особого способа найти свой охватывающий класс. Решение, которое у вас уже есть, является рекомендуемым способом.
Если у вас есть расширенный сценарий, и если вы готовы поддерживать не переносимый код, и если стоимость хранения дополнительного указателя достаточно важна для использования рискованного решения, то существует способ, основанный на объектной модели C++, С некоторыми оговорками, в которые я не буду вдаваться, вы можете полагаться на вложенные и вложенные классы, которые выкладываются в памяти в предсказуемом порядке, и существует фиксированное смещение между началом охватывающих и вложенных классов.
Код выглядит примерно так:
CEnclosing e;
int offset = (char*)&e.nested - (char*)&e;
//... inside nested class
CEnclosing* pencl = (CEnclosing*)((char*)this - offset);
OTOH одинаково возможно, что макрос offsetof может просто сделать это за вас, но я не пробовал.
Если вы действительно хотите это сделать, прочитайте о стандартном стандартном стандарте.
offsetof
.
Я считаю, что следующее может быть переносимым; хотя это не безупречно. В частности, он не будет работать через виртуальное наследование.
Кроме того, я хотел бы указать, что это небезопасно, поскольку он будет с удовольствием скомпилирован, даже если член, который вы передаете, не соответствует той, которую вы вычисляете смещением:
#include <iostream>
template <typename C, typename T>
std::ptrdiff_t offsetof_impl(T C::* ptr) {
C c; // only works for default constructible classes
T* t = &(c.*ptr);
return reinterpret_cast<char*>(&c) - reinterpret_cast<char*>(t);
}
template <typename C, typename T, T C::* Ptr>
std::ptrdiff_t offsetof() {
static std::ptrdiff_t const Offset = offsetof_impl(Ptr);
return Offset;
}
template <typename C, typename T, T C::* Ptr>
C& get_enclosing(T& t) {
return *reinterpret_cast<C*>(reinterpret_cast<char*>(&t)
+ offsetof<C, T, Ptr>());
}
// Demo
struct E { int i; int j; };
int main() {
E e = { 3, 4 };
//
// BEWARE: get_enclosing<E, int, &E::j>(e.i); compiles ERRONEOUSLY too.
// ^ != ^
//
E& ref = get_enclosing<E, int, &E::j>(e.j);
std::cout << (void const*)&e << " " << (void const*)&ref << "\n";
return 0;
}
Тем не менее, он работает на этом упрощенном примере, который позволил мне найти 2 ошибки в моей первоначальной реализации (уже). Обращайтесь с осторожностью.
Ясный и простой ответ на ваш вопрос - нет, C++ 11 не имеет специальной функции для обработки вашего сценария. Но в C++ есть трюк, позволяющий вам сделать это:
Если CEnclosing не имеет виртуальной функции, указатель на nested
будет иметь то же значение, что и указатель на содержащий экземпляр. То есть:
(void*)&e == (void*)&e.nested
Это связано с тем, что nested
переменная является первой в классе CEnclosing.
Однако, поскольку у вас есть виртуальная функция в классе CEnclosing, все, что вам нужно сделать, это вычесть размер vtable из &e.nested
и у вас должен быть указатель на e
. Не забудьте правильно нарисовать!
EDIT: Как сказал Стефан Роллан, это опасное решение, и, честно говоря, я бы не использовал его, но это единственный способ (или трюк), который я мог бы придумать для доступа к закрывающему классу из вложенного класса. Лично я, вероятно, попытаюсь пересмотреть связь между этими двумя классами, если я действительно хочу оптимизировать использование памяти до уровня, о котором вы говорили.
CEnclosing
являетсяCEnclosing
стандартной компоновки (=> нет виртуальных функций), вы можете использоватьoffsetof
чтобы получить смещение члена,nested
в объект типаCEnclosing
. Я бы рекомендовал сделатьCNested
частным вложенным классом или как-то запретить создание экземпляров не внутри объектаCEnclosing
если вы хотите использоватьoffsetof
(такжеstatic_assert
дляstd::is_standard_layout
).