Здравствуйте, Sheridan, Вы писали:
S>А зачем может понадобиться писать чтото типа S>
S>class A
S>{
S> class B{};
S> class C{};
S>};
S>
S>?
S>Какие можно получить от этого бонусы?
Во-первых, область видимости вложенных классов A::B и A::C, а не NamespaceHelper::B или что-нибудь в этом роде (во многих случаях этого же можно добиться через typedef).
Во-вторых, неразрешённая текущим стандартом, но весьма распространённое расширение среди компиляторов (так же, кажется, это разрешат в C++0x) возможность вложенного класса обращаться к private-членам объемлющего класса.
можно же не определять прямо внутри класса а отдельно
но вот только методы которым нужно тело вложенного типа надо описывать
не внутри определения иначе какая то циклическая зависимость получается
типа так я мало знаю сипипи поэтому скорее всего не знаю лучший способ
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Sheridan, Вы писали:
S>А зачем может понадобиться писать что-то типа S>
S>class A
S>{
S> class B{};
S> class C{};
S>};
S>
S>?
S>Какие можно получить от этого бонусы?
Да больше геморроя, чем бонусов
Например, для объявления указателя на объект вложенного класса всегда потребуется подключать заголовок с полным определением всего обрамляющего класса, что может стать причиной появления лишних зависимостей на уровне заголовочных файлов и отрицательно сказаться на времени компиляции.
ИМХО, делать классы вложенными имеет смысл только тогда, когда эти классы являются деталями реализации обрамляющего класса и не предназначены для внешнего использования. (Иногда возникает необходимость изолировать какой-либо класс от внешнего использования). Такие классы следует делать закрытыми. При этом ВСЕГДА будет возможность оставить в теле обрамляющего класса только предварительное объявление такого класса, а его определение сделать отдельно.
Например, реализация идиомы pImpl (не компилировал):
Если же вложенный класс открыт — значит он предназначен для внешнего использования — значит он может использоваться отдельно от обрамляющего класса (хотя бы через указатели и ссылки) — значит это самостоятельная сущность и должна быть определена отдельно. ВСЕГДА есть возможность вынести определение вложенного класса наружу, заменив его определение в обрамляющем классе алиасом (typedef). Например:
AlexeyF напомнил о существовании такой полезной возможности, как доступ к закрытым членам обрамляющего класса из вложенного. Я считаю, поскольку эта возможность не закреплена стандартом, то лучше ею и не пользоваться, благо для этого существуют отношения дружбы между классами.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали:
R>Да больше геморроя, чем бонусов
Не всё так однозначно, к сожалению.
Я, например, знаю, несколько относительно полезных применения.
1) Вынесение какого-то аспекта интерфейса класса. Ну, например, для CRTP часто требуется умение приводить базу к наследнику, а наследника к базе.
Если тебе нужно приватное или защищённое наследование, то с таким приведением будут проблемы. Можно, конечно, объявить базу своим другом. Но можно и не базу, а какую-то вложенную в базу структурку методы которой умеют осуществлять все нужные конверсии...
2) Когда есть система параметризованных чем-то одним и тем же классов. Как в примере регистратора типов для фабрики, который я приводил выше.
Трудно обеспечить одинаковость параметров, особенно если есть какая-то нетривиальная их трансляция...
А так всё зависимые классы делаешь вложенными и совместимость обеспечивается автоматически...
R>AlexeyF напомнил о существовании такой полезной возможности, как доступ к закрытым членам обрамляющего класса из вложенного. Я считаю, поскольку эта возможность не закреплена стандартом, то лучше ею и не пользоваться, благо для этого существуют отношения дружбы между классами.
Ну можно другом объявлять вложенные классы всегда. Тогда будет совместимо...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, rg45, Вы писали:
R>Здравствуйте, Sheridan, Вы писали:
S>>А зачем может понадобиться писать что-то типа S>>
S>>class A
S>>{
S>> class B{};
S>> class C{};
S>>};
S>>
S>>?
S>>Какие можно получить от этого бонусы?
R>... R>Если же вложенный класс открыт — значит он предназначен для внешнего использования — значит он может использоваться отдельно от обрамляющего класса (хотя бы через указатели и ссылки) — значит это самостоятельная сущность и должна быть определена отдельно. ВСЕГДА есть возможность вынести определение вложенного класса наружу, заменив его определение в обрамляющем классе алиасом (typedef). Например:
Можно вспомнить еще один близкий случай. Затруднения могут возникать с выносом вложенных ШАБЛОНОВ классов за пределы обрамляющих ШАБЛОННЫХ классов. Эти затруднения связаны с тем, что в стандарте 2003-го года не предусмотрено возможности объявлять шаблонные алиасы. Рассмотрим пример:
template<typename T>
class Outer
{
public:
template<typename Q>
class Inner
{
public:
T foo(const Q&);
};
};
Как видите, ситуация осложнена тем, что вложенный шаблонный класс Inner является зависимым от параметров шаблона внешнего класса Outer. В C++0x вынос Inner осуществляется легко и непринужденно:
template<typename T, typename Q>
class InnerBase
{
public:
T foo(const Q&);
};
template<typename T>
class Outer
{
public:
template<typename Q>
using Inner = InnerBase<T, Q>;
};
В рамках же стандарта 2003-го года это можно сделать так:
template<typename T, typename Q>
class InnerBase
{
public:
T foo(const Q&);
};
template<typename T>
class Outer
{
public:
template<typename Q>
struct Inner
{
typedef InnerBase<T, Q> Type;
};
};
"Ага!" — скажете вы — "все-таки пришлось определить вложенный класс!". Да, пришлось. Но, обратите внимание, что этот вложенный класс кроме определения алиаса не содержит больше никаких членов — ни функций, ни данных и объекты этого класса никогда не будут создаваться. Это такой, прием, позволяющий обойти ограничения языка, и это тот случай, когда использование вложенных классов оправдано.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, Erop, Вы писали:
E>Ну можно другом объявлять вложенные классы всегда. Тогда будет совместимо...
Увы:
11.4 Friends [class.friend]
1 A friend of a class is a function or class that is not a member of the class but is permitted to use the private
and protected member names from the class. The name of a friend is not in the scope of the class, and the
friend is not called with the member access operators (5.2.5) unless it is a member of another class.
...
Однако, в C++0x (смотрел в 2011-04-05) этого уже нет:
11.3 Friends [class.friend]
1 A friend of a class is a function or class that is given permission to use the private and protected member
names from the class. A class specifies its friends, if any, by way of friend declarations. Such declarations give
special access rights to the friends, but they do not make the nominated friends members of the befriending
class.
...
Здравствуйте, rg45, Вы писали:
R>AlexeyF напомнил о существовании такой полезной возможности, как доступ к закрытым членам обрамляющего класса из вложенного. Я считаю, поскольку эта возможность не закреплена стандартом, то лучше ею и не пользоваться, благо для этого существуют отношения дружбы между классами.
Починят в C++0x:
11.7 Nested classes [class.access.nest]
1 A nested class is a member and as such has the same access rights as any other member. The members of
an enclosing class have no special access to members of a nested class; the usual access rules (Clause 11)
shall be obeyed. [ Example:
class E {
int x;
class B { };
class I {
B b; // OK: E::I can access E::Bint y;
void f(E* p, int i) {
p->x = i; // OK: E::I can access E::x
}
};
int g(I* p) {
return p->y; // error: I::y is private
}
};
Здравствуйте, Erop, Вы писали:
E>2) Когда есть система параметризованных чем-то одним и тем же классов. Как в примере регистратора типов для фабрики, который я приводил выше. E>Трудно обеспечить одинаковость параметров, особенно если есть какая-то нетривиальная их трансляция... E>А так всё зависимые классы делаешь вложенными и совместимость обеспечивается автоматически...
Этот случай, как мне кажется, я рассматривал в посте рядом: здесь
, или нет?
E>Я, например, знаю, несколько относительно полезных применения. E>1) Вынесение какого-то аспекта интерфейса класса. Ну, например, для CRTP часто требуется умение приводить базу к наследнику, а наследника к базе. E>Если тебе нужно приватное или защищённое наследование, то с таким приведением будут проблемы. Можно, конечно, объявить базу своим другом. Но можно и не базу, а какую-то вложенную в базу структурку методы которой умеют осуществлять все нужные конверсии...
А этот случай "на слух" воспринимается тяжеловато. Не получается ли в этом случае вложенный класс сделать закрытым? Если получается, тогда это согласуется с тем, что я писал выше. Примерчик бы рассмотреть, простенький.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали:
E>>А так всё зависимые классы делаешь вложенными и совместимость обеспечивается автоматически... R>Этот случай, как мне кажется, я рассматривал в посте рядом: здесь
Ну что-то вроде того, но
1) Я не понимаю, чем твоё решение лучше просто вложенного класса. Если мы доступ к Inner всё равно осуществляем, как к ID вложенному в класс...
2) "Ты рассматривал" уже после того, как я запостил сообщение. Ну, во всяком случае, запостил после...
Я же всё-таки не телепат
E>>Я, например, знаю, несколько относительно полезных применения. E>>1) Вынесение какого-то аспекта интерфейса класса. Ну, например, для CRTP часто требуется умение приводить базу к наследнику, а наследника к базе. E>>Если тебе нужно приватное или защищённое наследование, то с таким приведением будут проблемы. Можно, конечно, объявить базу своим другом. Но можно и не базу, а какую-то вложенную в базу структурку методы которой умеют осуществлять все нужные конверсии...
R>А этот случай "на слух" воспринимается тяжеловато. Не получается ли в этом случае вложенный класс сделать закрытым? Если получается, тогда это согласуется с тем, что я писал выше. Примерчик бы рассмотреть, простенький.
Обычно в такой ситуации класс получается защищённым.
Но, в целом, если речь идёт о классе, через который передаются права делать что-то, то он, в общем сдучае, должен
struct SimpleListTag {};
template<typename TNode, typename TList = SimpleListTag> class CListBase {
protected:
// Все непубличные наследники CListBase и CNodeBase вплоть до TList и TNode должны иметь в друзьях этот класс.
// Он используется для преобразований TList и TNode в CListBase и CNodeBase и обратно.class TypeConvertor {
friend CListBase;
static const CListBase* toBase( const TList* list ) { return list; }
static CListBase* toBase( TList* list ) { return list; }
static const CNodeBase* toBase( const TNode* node ) { return node; }
static CNodeBase* toBase( TNode* node ) { return node; }
static TNode* toNode( CNodeBase* node ) { return static_cast<TNode*>( node ); }
static TList* toList( CListBase* list ) { return static_cast<TList*>( list ); }
};
private:
static const CNodeBase* toBase( const TNode* node ) { return TypeConvertor::toBase( presumePtr( node ) ); }
static CNodeBase* toBase( TNode* node ) { return TypeConvertor::toBase( presumePtr( node ) ); }
static CListBase* toBase( TList* list ) { return TypeConvertor::toBase( presumePtr( list ) ); }
};
template<typename TNode>
class CListBase<TNode, SimpleListTag> : public CListBase< TNode, CListBase<TNode> > {};
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Sheridan, Вы писали:
S>Приветствую!
S>А зачем может понадобиться писать чтото типа S>
S>class A
S>{
S> class B{};
S> class C{};
S>};
S>
S>?
S>Какие можно получить от этого бонусы?
На мой взгляд, нет никаких преимуществ, которые нельзя было бы реализовать без вложенных классов. Лично я используя вложенные классы только для логической группировки. Например, если я знаю, что класс B будет использоваться только внутри класса A, либо же не внутри, но так или иначе в контексте класса A, то я его объявляю вложенным. Просто так проще понимать архитектуру программы и получается в некотором роде самодокументированный код. Ну наверное самый яркий пример — это итераторы STL. Соверешнно очевидно, что нет никакого смысла объявлять переменную типа итератор вне контекста контейнера: если есть контейнер, есть и итератор. Нет контейнера — и итератор не нужен. Получается определенная логическая связь, связывающая между собой класс контейнера и класс итератора. И вложенние позволяет лишь объединить эти классы так сказать в одну группу. Хотя эти же итераторы можно было бы реализовать и отдельно, независимо от класса контейнера.
Здравствуйте, Erop, Вы писали:
E>Ну что-то вроде того, но E>1) Я не понимаю, чем твоё решение лучше просто вложенного класса. Если мы доступ к Inner всё равно осуществляем, как к ID вложенному в класс...
Не осуществляем, а можем осуществлять — это просто иллюстрация совместимости с исходным вариантом. Но можем и по-другому. После вынесения Inner за пределы класса мы получили возможность пользоваться только лишь предварительным объявлением этого класса, не видя определения класса Outer:
E>2) "Ты рассматривал" уже после того, как я запостил сообщение. Ну, во всяком случае, запостил после... E>Я же всё-таки не телепат
Извини, это я тормознул
E>>>1) Вынесение какого-то аспекта интерфейса класса. Ну, например, для CRTP часто требуется умение приводить базу к наследнику, а наследника к базе. E>>>Если тебе нужно приватное или защищённое наследование, то с таким приведением будут проблемы. Можно, конечно, объявить базу своим другом. Но можно и не базу, а какую-то вложенную в базу структурку методы которой умеют осуществлять все нужные конверсии...
R>>А этот случай "на слух" воспринимается тяжеловато. Не получается ли в этом случае вложенный класс сделать закрытым? Если получается, тогда это согласуется с тем, что я писал выше. Примерчик бы рассмотреть, простенький.
E>Обычно в такой ситуации класс получается защищённым. E>Но, в целом, если речь идёт о классе, через который передаются права делать что-то, то он, в общем сдучае, должен E>...
Этот фрагмент изучу чуть позже и отпишусь, сейчас просто немного занят.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали:
R>Не осуществляем, а можем осуществлять — это просто иллюстрация совместимости с исходным вариантом. Но можем и по-другому. После вынесения Inner за пределы класса мы получили возможность пользоваться только лишь предварительным объявлением этого класса, не видя определения класса Outer:
Шаблона, но не класса. Часто нужен конкретный класс, а не весь шаблон. Мало того, весь шаблон обычно не нужен.
Ну, например, я тебе приводил кусок интрузивного списка.
Каждый конкретный список друг базе своего элемента. Но не друг всем элементам всех списков.
Мало того, там есть нетривиальная обработка одного из параметров.
При твоём подходе пришлось бы её повторить в двух местах...
На самом деле ту проблему, которая беспокоит тебя, можно решить и обратным способом.
Сделать во внешнем scope шаблон класса, выведенного из nested класса. Тогда будут доступны всякие fjrward-декларации.
Правда это будет уже близко к тому, что ты называешь приватным nested классом.
R>Извини, это я тормознул R>Этот фрагмент изучу чуть позже и отпишусь, сейчас просто немного занят.
ok
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Sheridan, Вы писали:
S>Приветствую!
S>А зачем может понадобиться писать чтото типа
S>
//SFENAEtemplate <typename type>
no_type is_has_connector_func(...);
template <typename type>
yes_type is_has_connector_func(typename type::some_connector* ptr = 0);
template <typename type> struct is_has_connector { enum { value = sizeof(is_has_connector_func<type>()) == sizeof(yes_type) };};
template <int I > struct demo_storage;
/*
* For compile time functors
*/struct some_type0
{
struct some_connector
{
/*
* Implementation
*/
};
...
};
//Регистрация типа в компайл таймовый сторадж? из которого потом можно выгрести все зарегистрированные?
//При желании процесс сбора зарегистирированных типов можно автоматизировать и собрать по всему стораджу проекта //
register_type_2storage(demo_storage, some_type0);
struct some_type1
{
struct some_connector
{
/*
* Implementation
*/
};
...
};
register_type_2storage(demo_storage, some_type1);
//... Many different classes registered template <typename type>
struct checker {
enum {
value = is_has_connector<type>::value && custom_checkers<type> ... ||
тут можно свои дополнительные проверки имплементировать
};
};
/*
* TT - define to type list initialize
*/template <typename worker_type_>
void worker_run (worker_type_ worker)
{
//Тут генерируем тайплист и выбираем из зарегистрированных типов только подходящие под кокретный чеккер typedef typename type_list_maker<checker, typename generate_type_list<demo_storage>::result >::result connectors;
runtime_type_list_iterator<connectors>::run(worker);
}
S>
Бонус собственно простой все архитектурные операции, связывания и тд происходят в компайл тайме, разработку можно переводить на достаточно высокий уровень абстракции, когда вложенный тип будет функтором определяющим критерии поиска, вызовов .. собственно вложенный тип уже и есть член класса — компайл таймовый метод со всеми вытекающими.
Compiler can be as trained AI but can't compose music.
Antheil piano jazz sonata. Я болен ПГМ.