Как нам реорганизовать С++

Apr 24, 2024 12:47



Понадобилось мне давеча сделать нечто хитрое с помощью взаимно-рекурсивных классов.

Только для сиплюсплюсников )

work

Leave a comment

Comments 40

pigdeon April 24 2024, 21:11:14 UTC
Это тяжелое наследие царизма, точнее языка "С", где все элементы структуры должны иметь известный на момент компиляции размер. Вот и пришлось через темплейты ходить, что, в-общем и правильно, если мы пытаемся сохранять семантику.

Reply

spamsink April 24 2024, 21:38:48 UTC
Это тяжелое наследие царизма, точнее языка "С"

Именно, но, как говорится, пора бы и перестать.

Reply


yigal_s April 24 2024, 21:34:40 UTC
интересный код.

Но я бы сказал, это похоже на мелкий недостаток, который легко обойти. И обходить который нужно не так уж часто.
Возможно, комитет не захочет дополнительно усложнять спек языка для подобного случая.

Reply

spamsink April 24 2024, 21:44:35 UTC
"Мелкие недостатки", накапливаясь, создают барьер для эффективного практического использования языка. Если у программиста половина времени будет уходить на промпты к какому-нибудь ИИ вида "Я хочу сделать ХХХ, а оно вот так не компилируется. Как это правильно написать?", ничего хорошего из этого не выйдет.

Reply

yatur April 25 2024, 05:03:30 UTC
Когда не компилируется - это еще полбеды. Хуже, когда написана какая-то ересь, которая сразу и не скажешь, как работает - ну типа вашего примера. Еще хуже, когда компилируется и делает что-то неожиданное. Типа

vector x(2); - создает вектор [0,0]

vector x{2}; - создает вектор [2]

С++ полон таких мин и сиплюсплюсники, похоже, жутко гордятся знанием их карты и умением их обходить :) А некоторые с удовольствием изобретают новые :D

Reply

spamsink April 25 2024, 05:24:30 UTC
Это, конечно, смешно, но книгу "Ошибки-ловушки при программировании на Х", наверное, можно написать практически про любой индустриальный язык Х. Начиная как минимум с Фортрана.

Изобретают новые мины весьма некоторые люди, это да.

Reply


rezkiy April 25 2024, 02:35:09 UTC
ну как, у вас на момент // !!! Foo не определен но например vector(Foo) вполне даже определен, а array(Foo) cнова нет. То есть мы определились, что без глубокого вникания неясно, темплит у нас или нет. А если неясно темплит или нет, то какие имена dependent a какие нет? Это важно потому что two phase name lookup.

Reply

spamsink April 25 2024, 03:40:31 UTC
Так ведь это "глубокое вникание" и происходит сейчас, когда парсится non-template определение класса. Вот если при этом парсинге возникли ошибки, и все они - типа "incomplete class Foo" или являющиеся следствиями из них (если хочется упростить, то "если при этом парсинге возникли ошибки, и среди них есть типа "incomplete class Foo"), то
  • делаем список упомянутых неполных классов,
  • парсим "template<список со словом class перед каждым именем>",
  • парсим текущее определение класса, подменяя исходное имя класса на что-то уникальное,
  • если всё равно есть ошибки, выдаем полученные при первой итерации парсинга, иначе парсим using исходное_имя_класса = уникальное_имя<список>;
  • продолжаем парсить исходник далее

Reply

yatur April 25 2024, 05:12:15 UTC
Такая магия (которой и так много в С++) обычно ни к чему хорошему не приводит. Начиная с того, что класс неожиданно окажется темплейтом, а значит его методы должны быть видны при компиляции. Типа

// bar.h
class Bar { // implicit template
public:
void doSomething();
};

// bar.cpp
void Bar::doSomething() {... stuff... };

// usingBar.cpp
Bar b; // does not compile -- what are the template arguments?
Bar b; // OK
b.doSomething(); // the method is undefined

Далее, будут всякие приколы с template parameter inference:

class Bar { // implicit template on Foo
public:
Bar(const Foo& theFoo) {...}
};

class Foo {
public:
Foo(int arg) {...}
};

Bar bla(42); // if not template, a temporary Foo is constructed from 42
// if template, we are dealing with Bar now

Reply

spamsink April 25 2024, 05:27:40 UTC
С какого бы рожна class Bar { public: void doSomething(); }; вдруг стал implicit template, если его декларация и так отлично компилируется? Вы, похоже, так и не поняли, чего именно я хочу.

Reply


yatur April 25 2024, 03:54:08 UTC
Логика тут примерно такая:

1. Класс Foo остается неопределенным вплоть до закрывающей фигурной скобочки определения Foo.
2. Когда мы объявляем поле класса, компилятору нужно знать его размер в байтах.
3. Соответственно, если T не определен, то объявить поле типа T нельзя, но можно объявить поле типа T* или T&.
4. Это правило рекурсивно: vector написан так, что не исползует объекты типа T, а только типа T*.
Поэтому можно объявить поля типа vector, где T не определен, и компилятор доволен.

class Foo;
std::cout << sizeof(std::vector); // работает, печатает "24" в моей системе

Так что, компилятор не может "делать вид, что это темплейт", он должен знать, какой именно это темплейт. Если там внутре при объявлении полей используется сам T, то дело плохо. А если только указатели или ссылки на T - то можно.

Reply

spamsink April 25 2024, 04:07:01 UTC
Это все я прекрасно знаю. См. https://spamsink.livejournal.com/921363.html?thread=13365011#t13365011

Reply


yatur April 25 2024, 03:58:20 UTC
> какого бы, собственно, рожна нас в XXI веке заставляют прыгать через обруч

В этом вся суть С++ :) Остальные языки подсовывают вам обруч неявно, даже если он не очень нужен. В остальных языках почти все на свете - это на самом деле указатель, поэтому можно использовать рекурсивные классы направо и налево.

В С++ же, если вам не нужен обруч, можете не прыгать. Ну а если нужен, то не обессудьте, придется попросить обруч явно. Хорошо это или плохо - вопрос философский. С следует этой парадигме неукоснительно, а С++, ИМХО, только делает вид, а на самом деле таких обручей, бывает, подсунет, что не знаешь, куда от них деваться. Типа неявных вызовов copy constructor'а, которых никто не просил.

Reply

spamsink April 25 2024, 04:13:03 UTC
Хорошо это или плохо - вопрос философский.

https://spamsink.livejournal.com/921363.html?thread=13364499#t13364499

Reply


Leave a comment

Up