[С++1z] Автоматические функции и переменные
От: Went  
Дата: 19.11.13 19:39
Оценка: 2 (1)
Здравствуйте. Если придумали такие замечательные функциональные постфиксы как delete, default, final, override и т.п., то почему бы не нагрузить популярный сейчас auto новой функциональностью?
Помеченная таким словом функция — это автоматический шаблон, параметризируемый "верхним" типом объекта, для которого генерируется. Проще говоря, это функция, которая автоматически генерируется для каждого класса-потомка.
Такая функция вполне может быть виртуальной, так как ее сигнатура не меняется от предка к наследнику. Она может быть и настоящим шаблоном, но понятно, что не-виртуальным.
struct X
{
  virtual X* clone() const auto
  {
    return new decltype(*this)(*this);
  }
};

struct Y : X
{
};

int main()
{
  X* y = new Y;
  X* y2 = y->clone();
}


А можно и автоматические переменные, почему бы и нет?
struct X
{
  X() auto
  {
    s_objects.insert(this);
  }

  ~X() auto
  {
    s_objects.erase(this);
  }

  static std::set<auto*> s_objects;
}


Для чего это может быть полезно? В основном, для всяких автоматизаций, простейшей авторефлексии (все потомки класса X умеют себя клонировать, сравнивать, регистрировать в фабриках и т.п.)
Re: [С++1z] Автоматические функции и переменные
От: uzhas Ниоткуда  
Дата: 20.11.13 06:58
Оценка: :))) :))) :)))
Здравствуйте, Went, Вы писали:

W> почему бы не нагрузить популярный сейчас auto новой функциональностью?

In C++1z you just write "auto auto(auto auto) { auto; }". The compiler infers the rest from context.

Re: [С++1z] Автоматические функции и переменные
От: Кодт Россия  
Дата: 20.11.13 09:17
Оценка:
Здравствуйте, Went, Вы писали:

W>Здравствуйте. Если придумали такие замечательные функциональные постфиксы как delete, default, final, override и т.п., то почему бы не нагрузить популярный сейчас auto новой функциональностью?

W>Помеченная таким словом функция — это автоматический шаблон, параметризируемый "верхним" типом объекта, для которого генерируется. Проще говоря, это функция, которая автоматически генерируется для каждого класса-потомка.
<>
W>Для чего это может быть полезно? В основном, для всяких автоматизаций, простейшей авторефлексии (все потомки класса X умеют себя клонировать, сравнивать, регистрировать в фабриках и т.п.)

Да вообще, пора уже наряду с шаблонами завести дженерики.
А заодно — ко-контра-вариантность, тайпклассы (рантайм-версия трейтсов), и не замахнуться ли вообще на Хиндли нашего Миллера? </troll>

Но всё равно, дженерики и тайпклассы из коробки — было бы круто.
Перекуём баги на фичи!
Re[2]: [С++1z] Автоматические функции и переменные
От: Went  
Дата: 20.11.13 09:51
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Да вообще, пора уже наряду с шаблонами завести дженерики.

К>А заодно — ко-контра-вариантность, тайпклассы (рантайм-версия трейтсов), и не замахнуться ли вообще на Хиндли нашего Миллера? </troll>
К>Но всё равно, дженерики и тайпклассы из коробки — было бы круто.

Чувствую себя идиотом. Где можно почитать про все эти страшные слова?
Re[2]: [С++1z] Автоматические функции и переменные
От: Evgeny.Panasyuk Россия  
Дата: 20.11.13 10:11
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Но всё равно, дженерики и тайпклассы из коробки — было бы круто.


concepts, late binding.
Re[2]: [С++1z] Автоматические функции и переменные
От: uzhas Ниоткуда  
Дата: 20.11.13 10:54
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Но всё равно, дженерики и тайпклассы из коробки — было бы круто.


есть идея поискать другую коробку
Re[3]: [С++1z] Автоматические функции и переменные
От: Кодт Россия  
Дата: 20.11.13 11:33
Оценка: 6 (2)
Здравствуйте, Went, Вы писали:

W>Здравствуйте, Кодт, Вы писали:


К>>Да вообще, пора уже наряду с шаблонами завести дженерики.

К>>А заодно — ко-контра-вариантность, тайпклассы (рантайм-версия трейтсов), и не замахнуться ли вообще на Хиндли нашего Миллера? </troll>
К>>Но всё равно, дженерики и тайпклассы из коробки — было бы круто.

W>Чувствую себя идиотом. Где можно почитать про все эти страшные слова?


Не претендуя на полноту и предельную точность...

Дженерики — в яве и дотнете.
Это почти те же шаблоны, с той разницей, что для каждого набора параметров не генерируется новый код.
Очень грубо говоря,
// реализация дженерика
struct foo_base
{
  void* x_; // sizeof(x_) не зависит от типа
  void* bar_() { return x_; }
  virtual void buz_(void* z) { x_ = z; }
};

// типобезопасный фасад дженерика
template<class T> struct foo : foo_base
{
  T* bar() { return (T*)bar_(); }
  void buz(T* z) { buz_(z); }
};


Ковариантность и контравариантность — обобщение на функции и контейнеры (указатели, массивы, потоки) принципа подстановки Лисков.
См. яву сотоварищи.
В С++ оно ярко проявляется с указателями на члены.
struct Foo {};
struct Bar : Foo {};

// немножко реинтерпрет-кастов...
// ковариантность
Bar* make_bar();
Foo* (*make_some_foo)() = ((Foo*)(*)()) make_bar; // так делать можно, потому что make_bar возвращает какой-то субкласс Foo и следует LSP

// контравариантность
void take_foo(Foo*);
void (*take_some_bar)(Bar*) = (void(*)(Bar*)) take_foo; // так тоже можно сделать, потому что take_foo является частным случаем take_some_bar

// а вот и их честные аналоги безо всяких хаков
Bar* point_to_bar;
Foo* point_to_some_foo = point_to_bar; // наследника можно привести к предку

void Foo::the_method_of_foo();
void (Bar::*some_method_of_bar)() = &Foo::the_method_of_foo; // у наследника можно вызвать метод предка, поэтому метод предка приводится к наследнику

Ну и с контейнерами (здесь — контейнер одиночного элемента, сиречь, указатель)
struct Foo       { void f(); };
struct Bar : Foo { void g(); };
struct Buz : Bar { voig h(); };

Foo* foo = new Foo();
Bar* bar = new Bar();
Buz* buz = new Buz();

// ковариантность по in-параметру
void take_bar(Bar* x) { x->g(); }
take_bar((Bar*)foo); // это запрещено, в т.ч. системой типов С++, иначе будет foo->g()
take_bar(      bar);
take_bar(      buz); // LSP в чистом виде

// контравариантность по out-параметру
void give_bar(Bar*& x); { x = rand() ? new Bar() : new Buz(); }
give_bar((Bar*&)foo); // foo может быть и Bar, и Buz (но С++ требует хакнуть тип)
give_bar(       bar); // bar тоже
give_bar((Bar*&)buz); // а вот это запрещено! иначе buz = new Bar

// инвариантность по inout-параметру
void edit_bar(Bar*& x) { take_bar(x); give_bar(x); }
edit_bar((Bar*&)foo); // нельзя, т.к. foo->g()
edit_bar(       bar);
edit_bar((Bar*&)buz); // нельзя, т.к. buz = new Bar


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

В системе F эти уравнения однозначны всегда. Поэтому перегрузки функций должны иметь однообразные сигнатуры.
Если, скажем, есть оператор сдвига int operator<<(int,int), то его можно обобщить до T operator<<(T,T), и туда уже не впишется ostream& operator<<(ostream&,чтопопало).

Тайпклассы (typeclass) — см. язык haskell.
В некотором роде, это способ отделить виртуальные функции от экземпляров.
struct Foo;
struct IFooMethods;

typedef pair<Foo*,IFooMethods const*> FFF;
struct IFooMethods
{
  virtual FFF create() const = 0;
  virtual FFF clone(FFF this_) const = 0;
  virtual void unary(FFF this_) const = 0;
  virtual void binary(FFF this_, FFF other_) const = 0;
};

struct Foo
{
  void unary();
  void binary(FFF other) { other.second_->unary(other); } // виртуальный вызов other->unary()
};
struct FooMethods : IFooMethods
{
  static FooMethods methods;
  FFF  create()                      const { return FFF(new Foo(), &methods); }
  FFF  clone(FFF this_)              const { return FFF(new Foo(*this_.first), this); }
  void unary(FFF this_)              const { this_.first->unary(); }
  void binary(FFF this_, FFF other_) const { return this_.first->binary(other_); }
};

struct Bar : Foo { ..... };
struct BarMethods : FooMethods
{
  static BarMethods methods;
  FFF  create()                      const { return FFF(new Bar(), &methods); }
  FFF  clone(FFF this_)              const { return FFF(new Bar(*this_.first), &methods); }
};

FFF foo, bar, xz;
foo = FooMethods::create();
bar = BarMethods::create();
xz = rand() ? foo.second->clone(foo)
   : rand() ? bar.second->clone(bar) // clone - штатный для ООП приём
            : bar.second->create();  // а вот create по мотивам - уже не совсем...

foo.second->unary(foo);
foo.second->binary(foo, bar);

Лисп и хаскелл делают это прозрачно, а в С++ приходится громоздить страшное. Так же, как в Си приходится громоздить страшное для реализации обычных виртуальных функций (например, COM-объектов).
Ну и, разумеется, в хаскелле тайпклассы участвуют в системе типов.
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.