vak: (Default)
Serge Vakulenko ([personal profile] vak) wrote2024-06-23 11:54 am

Рекурсивные типы в Си++

Оказывается, в Си++ есть возможность определять рекурсивные типы. Представьте, что нам нужны списки вида std::list, где элементом списка может быть число, строка или тот же список.

Решение в лоб не работает:
using Integer = long long;
using String = std::string;
using List = std::list<Term>; ⟸ здесь Term не определено
using Term = std::variant<Integer, String, List>;
Поменяем местами, опять не годится:
using Integer = long long;
using String = std::string;
using Term = std::variant<Integer, String, List>; ⟸ здесь List не определено
using List = std::list<Term>;
Ситуация не безнадёжна, однако. Есть способ определить List отложенным образом:
#include <string>
#include <variant>
#include <list>
#include <iostream>

using Integer = long long;
using String  = std::string;
struct List;
using Term    = std::variant<Integer, String, List>;

struct List : public std::list<Term> {};

int main()
{
    std::cout << "sizeof Integer = " << sizeof(Integer) << '\n';
    std::cout << "sizeof String  = " << sizeof(String) << '\n';
    std::cout << "sizeof List    = " << sizeof(List) << '\n';
    std::cout << "sizeof Term    = " << sizeof(Term) << '\n';
}
Эта программа выдаёт:
sizeof Integer = 8
sizeof String  = 24
sizeof List    = 24
sizeof Term    = 32

[personal profile] flamedancerii 2024-06-23 08:02 pm (UTC)(link)
Семён Семёныч - ну это ж классика, declaration vs definition.
Техника называется forward declaration.

Добавлено:
Ну это как в самом файле сначала написать (хоть много раз)
extern int a;

потом
void f() {
a=0;
}

потом

int a;

И я похоже отстал от жизни - forward declaration оказывается устаревший термин, просто declaration покрывает кейс.
https://stackoverflow.com/questions/4926105/what-is-forward-declaration-in-c/4926698#4926698
Edited 2024-06-23 20:10 (UTC)
juan_gandhi: (Default)

[personal profile] juan_gandhi 2024-06-23 08:40 pm (UTC)(link)

О, что-то очень знакомое. Вот этот трюк с рекурсией.

sab123: (Default)

[personal profile] sab123 2024-06-24 07:47 pm (UTC)(link)
Или напрямую использовать указатель/ссылку. Объявление вперед реботает только потому, что внутри там используется не объект напрямую, а ссылка на него, поэтому размер контейнера не зависит от размера объекта.
spamsink: (Default)

[personal profile] spamsink 2024-06-25 04:42 am (UTC)(link)
См. https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

Мне по работе пришлось сделать практически то же самое, что ты описываешь, только с векторами вместо списков, буквально пару-тройку месяцев назад.
spamsink: (Default)

[personal profile] spamsink 2024-06-25 09:51 pm (UTC)(link)
Можно, конечно, попытаться передрать какую-нибудь компактную реализацию Рефала с Си на Паскаль...