Вложенные классы... Зачем?
От: Sheridan Россия  
Дата: 20.04.11 18:59
Оценка:
Приветствую!

А зачем может понадобиться писать чтото типа
class A 
{
  class B{};
  class C{};
};

?

Какие можно получить от этого бонусы?
avalon 1.0rc3 rev 306, zlib 1.2.5 (17.12.2009 01:06:14 MSK +03:00)(Qt 4.6.0)
Matrix has you...
Re: Вложенные классы... Зачем?
От: Alexey F  
Дата: 20.04.11 19:06
Оценка: +1
Здравствуйте, 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-членам объемлющего класса.
Re: Вложенные классы... Зачем?
От: Ytz https://github.com/mtrempoltsev
Дата: 20.04.11 19:15
Оценка: 13 (2) +3
namespace std
{
    template <class T>
    class vector
    {
        class iterator
        {
            ...
        };
        ...
    };
    ...
};

std::vector<int>::iterator it;
Re: Вложенные классы... Зачем?
От: jyuyjiyuijyu  
Дата: 20.04.11 19:23
Оценка:
Здравствуйте, Sheridan, Вы писали:

можно же не определять прямо внутри класса а отдельно
но вот только методы которым нужно тело вложенного типа надо описывать
не внутри определения иначе какая то циклическая зависимость получается
типа так я мало знаю сипипи поэтому скорее всего не знаю лучший способ
struct fake
{
    struct nested;
    void uu(nested obj);
};

struct fake::nested
{
    void uu(fake obj);
};

void fake::uu(nested obj){}
void fake::nested::uu(fake obj){}
Re[2]: Вложенные классы... Зачем?
От: Sheridan Россия  
Дата: 20.04.11 19:45
Оценка:
Приветствую, Ytz, вы писали:

Ytz>
Ytz> namespace std
Ytz> {
Ytz>     template <class T>
Ytz>     class vector
Ytz>     {
Ytz>         class iterator
Ytz>         {
Ytz>             ...
Ytz>         };
Ytz>         ...
Ytz>     };
Ytz>     ...
Ytz> };

Ytz> std::vector<int>::iterator it;
Ytz>


Ясно, спасибо.
avalon 1.0rc3 rev 306, zlib 1.2.5 (17.12.2009 01:06:14 MSK +03:00)(Qt 4.6.0)
Matrix has you...
Re: Вложенные классы... Зачем?
От: Erop Россия  
Дата: 20.04.11 21:20
Оценка:
Здравствуйте, Sheridan, Вы писали:

S>Какие можно получить от этого бонусы?


Ну, например, такие
Автор: Erop
Дата: 26.04.08
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Вложенные классы... Зачем?
От: Vain Россия google.ru
Дата: 20.04.11 22:19
Оценка: 1 (1)
Здравствуйте, Ytz, Вы писали:

Ytz>
Ytz>namespace std
Ytz>{
Ytz>    template <class T>
Ytz>    class vector
Ytz>    {
Ytz>        class iterator
Ytz>        {
Ytz>            ...
Ytz>        };
Ytz>        ...
Ytz>    };
Ytz>    ...
Ytz>};

Ytz>std::vector<int>::iterator it;
Ytz>

Зачем лишний таб вправо?
namespace std
{
    template <class T>
    class vector_iterator
    {
    };

    template <class T>
    class vector
    {
        friend class vector_iterator<T>;
        typedef vector_iterator<T> iterator;
        ...
    };
    ...
};

std::vector<int>::iterator it;
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re: Вложенные классы... Зачем?
От: rg45 СССР  
Дата: 21.04.11 05:30
Оценка:
Здравствуйте, Sheridan, Вы писали:

S>А зачем может понадобиться писать что-то типа

S>
S>class A 
S>{
S>  class B{};
S>  class C{};
S>};
S>

S>?

S>Какие можно получить от этого бонусы?


Да больше геморроя, чем бонусов

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

ИМХО, делать классы вложенными имеет смысл только тогда, когда эти классы являются деталями реализации обрамляющего класса и не предназначены для внешнего использования. (Иногда возникает необходимость изолировать какой-либо класс от внешнего использования). Такие классы следует делать закрытыми. При этом ВСЕГДА будет возможность оставить в теле обрамляющего класса только предварительное объявление такого класса, а его определение сделать отдельно.

Например, реализация идиомы pImpl (не компилировал):

//Foo.hpp
//-------

class Foo
{
public:
  explicit Foo(const std::string& some_text);

  void bar();

  virtual ~Foo();

private:
  class Impl;
  shared_ptr<Impl> m;
};

//Foo.cpp
//-------

class Foo::Impl
{
public: 

  const std::string& text;

  Impl(const std::string& text) : text(text) { }
};

void Foo::bar()
{
  std::cout << m->text << std::endl;
}


Еще пример, класс реализует некий абстрактный интерфейс, но доступ к классу реализации не дает (не компилировал):

//IFoo.hpp
//--------

class IFoo
{
public:
  virtual void foo() = 0;   
};

//Bar.hpp
//-------

class Bar
{
public:

  shared_ptr<IFoo> create_foo();

private:
  class Foo;
};

//Bar.cpp
//-------

class Bar::Foo : public IFoo
{
public:
  Foo(Bar* owner) {/*...*/}  

  virtual void foo() {/*...*/}
};

shared_ptr<IFoo> Bar::create_foo()
{
  return shared_ptr<Foo>(new Foo(this));
}


Если же вложенный класс открыт — значит он предназначен для внешнего использования — значит он может использоваться отдельно от обрамляющего класса (хотя бы через указатели и ссылки) — значит это самостоятельная сущность и должна быть определена отдельно. ВСЕГДА есть возможность вынести определение вложенного класса наружу, заменив его определение в обрамляющем классе алиасом (typedef). Например:

template<typename T>
class random_access_iterator { /*...*/ };

template<typename T>
class vector
{
public:
#if _DEBUG
  typedef random_access_iterator iterator;
#else
  typedef T* iterator;
#endif

/*...*/
};


AlexeyF напомнил о существовании такой полезной возможности, как доступ к закрытым членам обрамляющего класса из вложенного. Я считаю, поскольку эта возможность не закреплена стандартом, то лучше ею и не пользоваться, благо для этого существуют отношения дружбы между классами.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: Вложенные классы... Зачем?
От: Erop Россия  
Дата: 21.04.11 06:18
Оценка:
Здравствуйте, rg45, Вы писали:

R>Да больше геморроя, чем бонусов

Не всё так однозначно, к сожалению.

Я, например, знаю, несколько относительно полезных применения.
1) Вынесение какого-то аспекта интерфейса класса. Ну, например, для CRTP часто требуется умение приводить базу к наследнику, а наследника к базе.
Если тебе нужно приватное или защищённое наследование, то с таким приведением будут проблемы. Можно, конечно, объявить базу своим другом. Но можно и не базу, а какую-то вложенную в базу структурку методы которой умеют осуществлять все нужные конверсии...

2) Когда есть система параметризованных чем-то одним и тем же классов. Как в примере регистратора типов для фабрики, который я приводил выше.
Трудно обеспечить одинаковость параметров, особенно если есть какая-то нетривиальная их трансляция...
А так всё зависимые классы делаешь вложенными и совместимость обеспечивается автоматически...

R>AlexeyF напомнил о существовании такой полезной возможности, как доступ к закрытым членам обрамляющего класса из вложенного. Я считаю, поскольку эта возможность не закреплена стандартом, то лучше ею и не пользоваться, благо для этого существуют отношения дружбы между классами.


Ну можно другом объявлять вложенные классы всегда. Тогда будет совместимо...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Вложенные классы... Зачем?
От: rg45 СССР  
Дата: 21.04.11 06:37
Оценка:
Здравствуйте, 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;
  };
};


"Ага!" — скажете вы — "все-таки пришлось определить вложенный класс!". Да, пришлось. Но, обратите внимание, что этот вложенный класс кроме определения алиаса не содержит больше никаких членов — ни функций, ни данных и объекты этого класса никогда не будут создаваться. Это такой, прием, позволяющий обойти ограничения языка, и это тот случай, когда использование вложенных классов оправдано.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[3]: Вложенные классы... Зачем?
От: Alexey F  
Дата: 21.04.11 07:02
Оценка:
Здравствуйте, 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.
...

Re[2]: Вложенные классы... Зачем?
От: Alexey F  
Дата: 21.04.11 07:14
Оценка: 7 (1)
Здравствуйте, 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::B
        int 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
    }
};

—end example ]

Так что пользоваться будет можно, но потом
Re[3]: Вложенные классы... Зачем?
От: rg45 СССР  
Дата: 21.04.11 07:54
Оценка:
Здравствуйте, Erop, Вы писали:

E>2) Когда есть система параметризованных чем-то одним и тем же классов. Как в примере регистратора типов для фабрики, который я приводил выше.

E>Трудно обеспечить одинаковость параметров, особенно если есть какая-то нетривиальная их трансляция...
E>А так всё зависимые классы делаешь вложенными и совместимость обеспечивается автоматически...

Этот случай, как мне кажется, я рассматривал в посте рядом: здесь
Автор: rg45
Дата: 21.04.11
, или нет?

E>Я, например, знаю, несколько относительно полезных применения.

E>1) Вынесение какого-то аспекта интерфейса класса. Ну, например, для CRTP часто требуется умение приводить базу к наследнику, а наследника к базе.
E>Если тебе нужно приватное или защищённое наследование, то с таким приведением будут проблемы. Можно, конечно, объявить базу своим другом. Но можно и не базу, а какую-то вложенную в базу структурку методы которой умеют осуществлять все нужные конверсии...

А этот случай "на слух" воспринимается тяжеловато. Не получается ли в этом случае вложенный класс сделать закрытым? Если получается, тогда это согласуется с тем, что я писал выше. Примерчик бы рассмотреть, простенький.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: Вложенные классы... Зачем?
От: IROV..  
Дата: 21.04.11 09:42
Оценка:
Здравствуйте, Ytz, Вы писали:

Ytz>
Ytz>namespace std
Ytz>{
Ytz>    template <class T>
Ytz>    class vector
Ytz>    {
Ytz>        class iterator
Ytz>        {
Ytz>            ...
Ytz>        };
Ytz>        ...
Ytz>    };
Ytz>    ...
Ytz>};

Ytz>std::vector<int>::iterator it;
Ytz>


ВаСя делает это через

class vector
class _Vector_const_iterator
class _Vector_iterator

я не волшебник, я только учусь!
Re[4]: Вложенные классы... Зачем?
От: Erop Россия  
Дата: 21.04.11 14:14
Оценка:
Здравствуйте, rg45, Вы писали:

E>>А так всё зависимые классы делаешь вложенными и совместимость обеспечивается автоматически...

R>Этот случай, как мне кажется, я рассматривал в посте рядом: здесь
Автор: rg45
Дата: 21.04.11
, или нет?


Ну что-то вроде того, но
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> > {};
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Вложенные классы... Зачем?
От: ZegSoft Россия  
Дата: 21.04.11 14:45
Оценка: 1 (1) +1
Здравствуйте, Sheridan, Вы писали:

S>Приветствую!


S>А зачем может понадобиться писать чтото типа

S>
S>class A 
S>{
S>  class B{};
S>  class C{};
S>};
S>

S>?

S>Какие можно получить от этого бонусы?


На мой взгляд, нет никаких преимуществ, которые нельзя было бы реализовать без вложенных классов. Лично я используя вложенные классы только для логической группировки. Например, если я знаю, что класс B будет использоваться только внутри класса A, либо же не внутри, но так или иначе в контексте класса A, то я его объявляю вложенным. Просто так проще понимать архитектуру программы и получается в некотором роде самодокументированный код. Ну наверное самый яркий пример — это итераторы STL. Соверешнно очевидно, что нет никакого смысла объявлять переменную типа итератор вне контекста контейнера: если есть контейнер, есть и итератор. Нет контейнера — и итератор не нужен. Получается определенная логическая связь, связывающая между собой класс контейнера и класс итератора. И вложенние позволяет лишь объединить эти классы так сказать в одну группу. Хотя эти же итераторы можно было бы реализовать и отдельно, независимо от класса контейнера.
Re[5]: Вложенные классы... Зачем?
От: rg45 СССР  
Дата: 21.04.11 14:50
Оценка:
Здравствуйте, Erop, Вы писали:

E>Ну что-то вроде того, но

E>1) Я не понимаю, чем твоё решение лучше просто вложенного класса. Если мы доступ к Inner всё равно осуществляем, как к ID вложенному в класс...

Не осуществляем, а можем осуществлять — это просто иллюстрация совместимости с исходным вариантом. Но можем и по-другому. После вынесения Inner за пределы класса мы получили возможность пользоваться только лишь предварительным объявлением этого класса, не видя определения класса Outer:

//SomeOther.hpp

template<typename, typename> class InnerBase; //forward declaration

class SomeOther
{
public:

  typedef InnerBase<int, long> Foo;

  void bar(const Foo&);

private:
  shared_ptr<Foo> _foo;
};


E>2) "Ты рассматривал" уже после того, как я запостил сообщение. Ну, во всяком случае, запостил после...

E>Я же всё-таки не телепат

Извини, это я тормознул

E>>>1) Вынесение какого-то аспекта интерфейса класса. Ну, например, для CRTP часто требуется умение приводить базу к наследнику, а наследника к базе.

E>>>Если тебе нужно приватное или защищённое наследование, то с таким приведением будут проблемы. Можно, конечно, объявить базу своим другом. Но можно и не базу, а какую-то вложенную в базу структурку методы которой умеют осуществлять все нужные конверсии...

R>>А этот случай "на слух" воспринимается тяжеловато. Не получается ли в этом случае вложенный класс сделать закрытым? Если получается, тогда это согласуется с тем, что я писал выше. Примерчик бы рассмотреть, простенький.


E>Обычно в такой ситуации класс получается защищённым.

E>Но, в целом, если речь идёт о классе, через который передаются права делать что-то, то он, в общем сдучае, должен
E>...

Этот фрагмент изучу чуть позже и отпишусь, сейчас просто немного занят.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[6]: Вложенные классы... Зачем?
От: Erop Россия  
Дата: 21.04.11 15:09
Оценка:
Здравствуйте, rg45, Вы писали:

R>Не осуществляем, а можем осуществлять — это просто иллюстрация совместимости с исходным вариантом. Но можем и по-другому. После вынесения Inner за пределы класса мы получили возможность пользоваться только лишь предварительным объявлением этого класса, не видя определения класса Outer:


Шаблона, но не класса. Часто нужен конкретный класс, а не весь шаблон. Мало того, весь шаблон обычно не нужен.

Ну, например, я тебе приводил кусок интрузивного списка.
Каждый конкретный список друг базе своего элемента. Но не друг всем элементам всех списков.
Мало того, там есть нетривиальная обработка одного из параметров.
При твоём подходе пришлось бы её повторить в двух местах...

На самом деле ту проблему, которая беспокоит тебя, можно решить и обратным способом.
Сделать во внешнем scope шаблон класса, выведенного из nested класса. Тогда будут доступны всякие fjrward-декларации.
Правда это будет уже близко к тому, что ты называешь приватным nested классом.

R>Извини, это я тормознул

R>Этот фрагмент изучу чуть позже и отпишусь, сейчас просто немного занят.
ok
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Вложенные классы... Зачем?
От: ollv СССР https://youtu.be/DQDoYs6wHoo
Дата: 26.04.11 17:05
Оценка:
Здравствуйте, Sheridan, Вы писали:

S>Приветствую!


S>А зачем может понадобиться писать чтото типа


S>
    
   //SFENAE
   template <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. Я болен ПГМ.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.