1

Тема: Наслідування шаблонів. template<typename T, typename N = Node<T, N>>

Ідея така:
Є різні структури вузлів, які містять різну кількість вказівників на інші вузли такого ж типу як вони самі (в даному прикладі спрощено до двох: Node, DualNode). Всі вузли містять якісь дані, які загалом можуть бути довільного типу, тому я спочатку зробив з них наступні шаблони:

// Node.h
#pragma once

template<typename T>
struct Node
{
    T data;
    Node *next;
};

і

// DualNode.h
#pragma once

#include"Node.h"

template<typename T>
struct DualNode : public Node<T>
{
    DualNode *prev;
};

Виглядає наче все добре, але є проблема.
Якщо я створю екземпляр DualNode, то його поле next буде мати тип Node, а не DualNode:

DualNode<int> dnode;
dnode.next = new DualNode<int>(); // Це добре.
dnode.next = new Node<int>(); // Це погано.

Для того щоб рішити проблему, переписав шаблони так:

// Node.h
#pragma once

template<typename T, typename N = Node<T, N>>
struct Node
{
    T data;
    N *next;
};
// DualNode.h
#pragma once

#include"Node.h"

template<typename T, typename N = DualNode<T, N>>
struct DualNode : public Node<T, N>
{
    N *prev;
};

Але компілятор (Visual Studio 2013) видав, що не знає, що таке N

Error C2065: необявлений N.

Я спробував його забрати:

// Node.h
#pragma once

template<typename T, typename N = Node<T>>
struct Node
{
    T data;
    N *next;
};
// DualNode.h
#pragma once

#include"Node.h"

template<typename T, typename N = DualNode<T>>
struct DualNode : public Node<T, N>
{
    N *prev;
};

Компілятор видав, що це жорстоке поводження з компілятором

Error C1202: Надто складно, або рекурсія.

Ну я в тирнеті надибав приклади парних шаблонів, зробив по образу і подобію:

// Node.h
#pragma once

template<typename T, typename N = Node<T, N>>
struct Node
{
    T data;
    N *next;
};

template<typename T>
struct Node : public Node<T, Node<T>>
{ };
// DualNode.h
#pragma once

#include"Node.h"

template<typename T, typename N = DualNode<T, N>>
struct DualNode : public Node<T, N>
{
    N *prev;
};

template<typename T>
struct DualNode : public DualNode<T, DualNode<T>>
{ };

Компілятор:

Error C2955: для використання класу шаблон треба список аргументів.
Error C2976: мало аргументів.

І це печально.

Використовувати всюди static_cast, dynamic_cast і подібні для мене не варіант.

Чи є якийсь культурний спосіб описати ці структури так, щоб зберегти наслідування і гарантувати користувачам, що поле DualNode::next буде вказівником на DualNode ?
І як ?

Post's attachments

ConsoleApplication1.zip 12.92 kb, 250 downloads since 2016-05-15 

Подякували: Betterthanyou1

2

Re: Наслідування шаблонів. template<typename T, typename N = Node<T, N>>

template<typename T, typename N = Node<T, N>>
struct Node
{
    T data;
    N *next;
};

Спробуйте підставити якийсь конкретний тип і подивіться, чому дорівнює N в правій частині виразу. Власне, це і є проблемою - ви використовуєте два різні механізми узагальнення (ООП і шаблони), вимагаєте, щоб обидва працювали, і не пояснюєте, чому вам треба саме так. Взагалі мені здається, що ця конструкція має внутрішню суперечність, бо предку для інстанціації потрібне визначення нащадка. Може, ви щось не так зрозуміли, і вам таки треба повертати в обох випадках Node, тільки активніше використовувати поліморфізм? Чи, навпаки, не робити їх нащадками?

Подякували: leofun011

3

Re: Наслідування шаблонів. template<typename T, typename N = Node<T, N>>

Є такий патерн - CRTP. Наслідуючись від базового класу ви можете передавати йому самого нащадка як аргумент шаблону.
https://uk.wikipedia.org/wiki/%D0%94%D0 … 0%BE%D0%BD

В прикладі нижче наслідуваний класс передається в базовий як параметр, але для цього в шаблон Node треба добавити ще один аргумент крім аргументу типу даних.

Для того щоб можна було користуватись шаблоном Node без наслідуваного класу робиться часткова спеціалізація шаблону.
Можливо є більш елегантний варіант, без заглушки в частковій спеціалізаціі, але я його поки що не бачу.

#include <iostream>
#include <typeinfo>

// --- CRTP - curiously recurring template pattern

template<typename T, typename N >
struct Node
{
    T data;
    N *next;
};

template<typename T>
struct DualNode : public Node<T, DualNode<T>>
{
    DualNode *prev;
};

// --- Partial specialization Node

class Stub{};
template<typename T>
struct Node<T, Stub>
{
    T data;
    Node *next;
};

int main(int argc, char **argv)
{
  DualNode<int> a;
  std::cout << "a.prev is: " << typeid(a.prev).name() << std::endl;
  std::cout << "a.next is: " << typeid(a.next).name() << std::endl;

  Node<int, Stub> b;
  std::cout << "b.next is: " << typeid(b.next).name() << std::endl;
  return 0;
}
Подякували: koala, leofun01, ReAl3

4 Востаннє редагувалося -=ЮрА=- (16.05.2016 19:44:11)

Re: Наслідування шаблонів. template<typename T, typename N = Node<T, N>>

leofun01 у коді поста 1 невірно зроблена специфікація поінтерів у об'явах структур, тому і не парцювало, бо були поінтери не на темплейти а на void *

#include <iostream>
using namespace std;

template<typename T>
struct Node
{
    T        data;
    Node<T> *next;
};

template<typename T>
struct DualNode : public Node<T>
{
    DualNode<T> *prev;
};

int main(){
    DualNode<int> dnode;
    dnode.next = new DualNode<int>(); // Це добре.
    dnode.next = new Node<int>(); // Це погано(викреслити)
    return 0;
}

http://codepad.org/fafdDS3R

Output:
No errors or program output.

Подякували: leofun01, ReAl2

5

Re: Наслідування шаблонів. template<typename T, typename N = Node<T, N>>

Знайшов подібне питання на StackOverflow.
Це звичайно не те що мені було потрібно, але частково допомогло.

6

Re: Наслідування шаблонів. template<typename T, typename N = Node<T, N>>

Кодоґенерація макросами +CRTP - неймовірно потужна штука. Те, що в минулому займало 7 окремих файлів, тепер помістив в 1 файл (~50-100 рядків) :

// node.hpp
#ifndef NODE_HPP
#define NODE_HPP

#include <cstdint> // std::uint8_t
#include <utility> // std::move

#define DEFAULT_COPY_MOVE(NAME)                        \
    NAME(NAME const &node) = default;                  \
    NAME(NAME &&node) = default;                       \
    NAME &operator=(NAME const &node) = default;       \
    NAME &operator=(NAME &&node) = default;            \

template<typename T, std::uint8_t N, typename P>
struct node_t {
    T data = T();
    P *ptr[N] = { nullptr };
    node_t() = default;
    node_t(T const &data) : data(data) { }
    node_t(T &&data) : data(std::move(data)) { }
    virtual ~node_t() = default;
    DEFAULT_COPY_MOVE(node_t)
};

#define DECLARE_TEMPLATE(NAME)        \
template<typename T, std::uint8_t N>  \
struct NAME;                          \

#define GENERATE_TEMPLATE(NAME, PTR_TYPE_NAME)         \
DECLARE_TEMPLATE(PTR_TYPE_NAME)                        \
template<typename T, std::uint8_t N = 1>               \
struct NAME : node_t<T, N, PTR_TYPE_NAME<T, N>> {      \
    using base_t = node_t<T, N, PTR_TYPE_NAME<T, N>>;  \
    using base_t::data;                                \
    using base_t::ptr;                                 \
    NAME() = default;                                  \
    NAME(T const &data) : base_t(data) { }             \
    NAME(T &&data) : base_t(std::move(data)) { }       \
    virtual ~NAME() override = default;                \
    DEFAULT_COPY_MOVE(NAME)                            \
};                                                     \

/*// For editing only. Delete if not needed.
#define NAME node_this_t
#define PTR_TYPE_NAME node_other_t
GENERATE_TEMPLATE(PTR_TYPE_NAME, NAME)
template<typename T, std::uint8_t N = 1>               \
struct NAME : node_t<T, N, PTR_TYPE_NAME<T, N>> {      \
    using base_t = node_t<T, N, PTR_TYPE_NAME<T, N>>;  \
    using base_t::data;                                \
    using base_t::ptr;                                 \
    NAME() = default;                                  \
    NAME(T const &data) : base_t(data) { }             \
    NAME(T &&data) : base_t(std::move(data)) { }       \
    virtual ~NAME() override = default;                \
    DEFAULT_COPY_MOVE(NAME)                            \
};
#undef NAME
#undef PTR_TYPE_NAME
//*/

GENERATE_TEMPLATE(node_mono_t, node_mono_t)

GENERATE_TEMPLATE(node_duo_a_t, node_duo_b_t)
GENERATE_TEMPLATE(node_duo_b_t, node_duo_a_t)

GENERATE_TEMPLATE(node_trio_a_t, node_trio_b_t)
GENERATE_TEMPLATE(node_trio_b_t, node_trio_c_t)
GENERATE_TEMPLATE(node_trio_c_t, node_trio_a_t)

#undef DEFAULT_COPY_MOVE(NAME)
#undef DECLARE_TEMPLATE(NAME)
#undef GENERATE_TEMPLATE(NAME, PTR_TYPE_NAME)

#endif // NODE_HPP

Якось так.

Цей код дозволяє будувати такі структури (*.png, ~42 KB)
  • node_mono_t<T, 1> *stack_node;
    line-mono-1.png

  • node_mono_t<T, 2> *linked_list_node;
    line-mono-2.png

  • node_mono_t<T, 4> *matrix_node;
    quad-mono-4.png

  • node_duo_a_t<T, 2> *red_black_tree_node_red;
    node_duo_b_t<T, 2> *red_black_tree_node_black;

    bin-duo-2.png

  • node_duo_a_t<T, 3> *hex_grid_node_a;
    node_duo_b_t<T, 3> *hex_grid_node_b;

    hex-duo-3.png

  • node_trio_a_t<T, 3> *graph_node_a;
    node_trio_b_t<T, 3> *graph_node_b;
    node_trio_c_t<T, 3> *graph_node_c;

    dodeca-trio-3.png

Мій компілятор std::uint8_t і std::move нормально хавайе й без include'ів, але додав на всякий випадок.
Якщо комусь не подобається те, що виклики GENERATE_TEMPLATE(...) не закінчуються символом ;, то можете перенести (для struct NAME видалити, для викликів додати).
Ще маю сумніви чи правильно я зробив #undef. Може ставити дужки і параметри там не варто ?
Хто знає C++, дайте поради/рекомендації по коду.

Подякували: Chemist-i1

7

Re: Наслідування шаблонів. template<typename T, typename N = Node<T, N>>

З #undef достатньо лише ідентифікатора.

#undef DEFAULT_COPY_MOVE
#undef DECLARE_TEMPLATE
#undef GENERATE_TEMPLATE

Що ж, вітаю, ви засвоїли макроси у С++ й успішно застосували їх для кодогенерації. Ви за крок до ідеї, щоб реалізувати власну рефлексію з блекджеком та ромашками, як і будь-який (не)нормальний С++ програмер.

Прихований текст

Хоча, стоп, можливо, це лише я один такий..

Подякували: leofun011

8

Re: Наслідування шаблонів. template<typename T, typename N = Node<T, N>>

wander написав:

за крок до ідеї, щоб реалізувати власну рефлексію

Рефлексію останній раз бачив, коли працював з C#, до того з Java, в обох випадках вона була доступна з коробки. Як це може має виглядати в C++, поки не уявляю. Подумаю над цим на вихідних.

9

Re: Наслідування шаблонів. template<typename T, typename N = Node<T, N>>

https://i.imgflip.com/8frnlo.jpg
перепрошую

чекаю на аналогічний про зв'язні списки/графи в Rust

Подякували: leofun011

10

Re: Наслідування шаблонів. template<typename T, typename N = Node<T, N>>

Я не зміг згадати причину чому в минулому тримав спільного предка (node_t), тому я його видалив :

#define GENERATE_TEMPLATE(NAME, PTR_TYPE_NAME)   \
template<typename T, unsigned int N>             \
struct PTR_TYPE_NAME;                            \
template<typename T, unsigned int N>             \
struct NAME {                                    \
    static const decltype(N) ptr_count = N;      \
    using pointing_to_t = PTR_TYPE_NAME<T, N>;   \
    T data = T();                                \
    pointing_to_t *ptr[N] = { nullptr };         \
};                                               \

GENERATE_TEMPLATE(node_mono_t, node_mono_t)

GENERATE_TEMPLATE(node_duo_a_t, node_duo_b_t)
GENERATE_TEMPLATE(node_duo_b_t, node_duo_a_t)

GENERATE_TEMPLATE(node_trio_a_t, node_trio_b_t)
GENERATE_TEMPLATE(node_trio_b_t, node_trio_c_t)
GENERATE_TEMPLATE(node_trio_c_t, node_trio_a_t)

#undef GENERATE_TEMPLATE
Трохи довша версія (те саме)
// node.hpp
#ifndef NODE_HPP
#define NODE_HPP

#include <utility> // std::move

#define DEFAULT_CTOR_COPY_MOVE(NAME)             \
    NAME() = default;                            \
    NAME(NAME const &node) = default;            \
    NAME(NAME &&node) = default;                 \
    virtual ~NAME() = default;                   \
    NAME &operator=(NAME const &node) = default; \
    NAME &operator=(NAME &&node) = default;      \

#define DECLARE_TEMPLATE(NAME)        \
template<typename T, unsigned int N>  \
struct NAME;                          \

#define GENERATE_TEMPLATE(NAME, PTR_TYPE_NAME)   \
DECLARE_TEMPLATE(PTR_TYPE_NAME)                  \
template<typename T, unsigned int N>             \
struct NAME {                                    \
    static const decltype(N) ptr_count = N;      \
    using pointing_to_t = PTR_TYPE_NAME<T, N>;   \
    T data = T();                                \
    pointing_to_t *ptr[N] = { nullptr };         \
    NAME(T const &data) : data(data) { }         \
    NAME(T &&data) : data(std::move(data)) { }   \
    DEFAULT_CTOR_COPY_MOVE(NAME)                 \
};                                               \

/*// For editing only. Delete if not needed.
#define NAME node_this_t
#define PTR_TYPE_NAME node_other_t
GENERATE_TEMPLATE(PTR_TYPE_NAME, NAME)
template<typename T, unsigned int N>             \
struct NAME {                                    \
    static const decltype(N) ptr_count = N;      \
    using pointing_to_t = PTR_TYPE_NAME<T, N>;   \
    T data = T();                                \
    pointing_to_t *ptr[N] = { nullptr };         \
    NAME(T const &data) : data(data) { }         \
    NAME(T &&data) : data(std::move(data)) { }   \
    DEFAULT_CTOR_COPY_MOVE(NAME)                 \
};
#undef NAME
#undef PTR_TYPE_NAME
//*/

GENERATE_TEMPLATE(node_mono_t, node_mono_t)

GENERATE_TEMPLATE(node_duo_a_t, node_duo_b_t)
GENERATE_TEMPLATE(node_duo_b_t, node_duo_a_t)

GENERATE_TEMPLATE(node_trio_a_t, node_trio_b_t)
GENERATE_TEMPLATE(node_trio_b_t, node_trio_c_t)
GENERATE_TEMPLATE(node_trio_c_t, node_trio_a_t)

#undef DEFAULT_CTOR_COPY_MOVE
#undef DECLARE_TEMPLATE
#undef GENERATE_TEMPLATE

#endif // NODE_HPP

Але тут ще залишилась 1 проблема: всі вузли (a, b, c) містять дані (data) одного типу і я ше не придумав як це гарно порішати.

wander написав:

реалізувати власну рефлексію

std::reflect (Reflection Technical Specification ISO/IEC TS 23619) ?
Я не знаю шо там, ше не дивився. Додав в довгий список робіт, які треба зробити, але які ймовірно не будуть зроблені найблизчі кілька років.

koala написав:

чекаю на аналогічний про зв'язні списки/графи в Rust

Додав в той самий список.

11 Востаннє редагувалося Droid 77 (16.02.2024 22:43:04)

Re: Наслідування шаблонів. template<typename T, typename N = Node<T, N>>

leofun01 написав:

Але компілятор (Visual Studio 2013) видав, що не знає, що таке N

Стандарт плюсової мови який вирішили застосовувати?
Я обмежуюсь 11, іноді (як знань вистачає) 14.

12

Re: Наслідування шаблонів. template<typename T, typename N = Node<T, N>>

Droid 77 написав:

Стандарт плюсової мови який ...?

C++17, поки є така можливість, і мене вже попередили про заплановану заміну середовища, тому посилено вчу C++20.

13

Re: Наслідування шаблонів. template<typename T, typename N = Node<T, N>>

leofun01 написав:

std::reflect (Reflection Technical Specification ISO/IEC TS 23619) ?

Нє, ось це актуальний папір - Reflection for C++26  :)

Подякували: leofun011

14 Востаннє редагувалося steamwater (20.02.2024 14:52:43)

Re: Наслідування шаблонів. template<typename T, typename N = Node<T, N>>

Доброго дня, leofun01. Ви писали:

leofun01 написав:

Ідея така:
Є різні структури вузлів, які містять різну кількість вказівників на інші вузли такого ж типу як вони самі (в даному прикладі спрощено до двох: Node, DualNode). Всі вузли містять якісь дані, які загалом можуть бути довільного типу, тому я спочатку зробив з них наступні шаблони:

Здається, я не зрозумiв це ствердження:

leofun01 написав:

Всі вузли містять якісь дані, які загалом можуть бути довільного типу

Тобто, якщо

leofun01 написав:

вузли такого ж типу як вони самі

то це значить, що тип данних, що мicтиться у нодI має бути параметром шаблону структури (графу). Тобто, у межах одного инстансу такого графа, типи данных заданi при инстанцiiруваннi i змiнятись не можуть. Бо iнакше змiнився б тип ноду, а це протиречить першiй цiтатi. Ще раз по iншому спробую. Чи є простий контейнер накшалт

#include <iostream>
#include <list>

template <class T>
struct N_tree {
    N_tree(T const& data_) : data(data_) {};
    T data;
    std::list<N_tree<T>> list_nodes;
};
int main() {
    N_tree<int> n_tree_head(0);
    N_tree<int> n_tree_level1_1(1), n_tree_level1_2(2), n_tree_level1_3(3);

    n_tree_head.list_nodes.push_back(n_tree_level1_1);
    n_tree_head.list_nodes.push_back(n_tree_level1_2);
    n_tree_head.list_nodes.push_back(n_tree_level1_3);

    return 0;
}

чимось схожим на те що ви шукаєте?

Подякували: leofun011

15

Re: Наслідування шаблонів. template<typename T, typename N = Node<T, N>>

Впорядкував (вклав) типи, і подолав основну проблему. Тепер всі вузли містять дані відповідно до свого типу. І навіть у випадку якщо типи даних для різних шарів будуть повністю однакові, то строга типізація не дозволить присвоювати не правильний тип.

#define GENERATE_STRUCT(NAME, T, N, PTR_TYPE) \
struct NAME {                                 \
    static const decltype(N) ptr_count = N;   \
    using this_t = NAME;                      \
    using data_t = T;                         \
    using pointing_to_t = PTR_TYPE;           \
    data_t data = data_t();                   \
    pointing_to_t *ptr[N] = { nullptr };      \
};
#define TYPE(NAME) NAME##_t
#define GENERATE_TYPE(A, B) GENERATE_STRUCT(TYPE(A), T_##A, N_##A, TYPE(B))
#define USING(A, ...) using A = typename __VA_ARGS__::A;
#define ALIAS(A, ...) USING(TYPE(A), __VA_ARGS__) USING(A, __VA_ARGS__)
#define T(A, S) template<typename T_##A, unsigned int N_##A> struct A { S };
#define S(A, B, S) struct A; GENERATE_TYPE(A, B) struct A { S };
#define _(A) ::A<T_##A, N_##A>
#define A typename a
#define NS nodes

namespace NS {
T(a, GENERATE_TYPE(a, a)
    T(b, S(a, A::b, S(b, a, ALIAS(a, NS _(a)_(b)) ))
        T(c, S(a, A::b, S(b, A::b::c, S(c, a, ALIAS(a, NS _(a)_(b)_(c)) )))
        )))
};

#undef NS
#undef A
#undef _
#undef S
#undef T
#undef TYPE
#undef USING
#undef ALIAS
#undef GENERATE_TYPE
#undef GENERATE_STRUCT

Переслідую ціль - зробити так щоб користувачу було зручно.
Виявив що __VA_ARGS__ в деяких випадках таки можна користувати (C++11+), але +/- нормально з ним працювати можна тільки в C++20+.

Тут я перевіряв чи працюйуть типи так як я очікував. Звичайні асерти.
#include <cassert>
#include <typeinfo>
#include "node.hpp"

using namespace nodes;
void check_node_types() {
    const unsigned int n_a = 2;
    const unsigned int n_b = 3;
    const unsigned int n_c = 1;

    typedef a<short, n_a> mono;
    assert(typeid(mono::a_t) == typeid(mono::a_t::pointing_to_t));
    assert(mono::a_t::ptr_count == n_a);
    mono::a_t n1a;
    n1a.data = -0x7FFF; n1a.ptr[0] = &n1a;

    typedef a<short, n_a>::b<char, n_b> duo;
    assert(typeid(duo::a_t   ) == typeid(duo::a::b::a_t   ));
    assert(typeid(duo::a::b_t) == typeid(duo::a::b::a::b_t));
    assert(duo::a_t   ::ptr_count == n_a);
    assert(duo::a::b_t::ptr_count == n_b);
    duo::a_t    n2a;
    duo::a::b_t n2b;
    n2a.data = -0x7FFF; n2a.ptr[0] = &n2b;
    n2b.data = 'a'    ; n2b.ptr[0] = &n2a;

    typedef a<short, n_a>::b<char, n_b>::c<int, n_c> trio;
    assert(typeid(trio::a_t      ) == typeid(trio::a::b::c::a_t      ));
    assert(typeid(trio::a::b_t   ) == typeid(trio::a::b::c::a::b_t   ));
    assert(typeid(trio::a::b::c_t) == typeid(trio::a::b::c::a::b::c_t));
    assert(trio::a_t      ::ptr_count == n_a);
    assert(trio::a::b_t   ::ptr_count == n_b);
    assert(trio::a::b::c_t::ptr_count == n_c);
    trio::a_t       n3a;
    trio::a::b_t    n3b;
    trio::a::b::c_t n3c;
    n3a.data = -0x7FFF    ; n3a.ptr[0] = &n3b;
    n3b.data = 'a'        ; n3b.ptr[0] = &n3c;
    n3c.data = -0x7FFFFFFF; n3c.ptr[0] = &n3a;
}

Зараз там доступні 3 шари. Якщо треба 4-тий, то в середню частину додаємо рядок :

namespace NS {
T(a, GENERATE_TYPE(a, a)
    T(b, S(a, A::b, S(b, a, ALIAS(a, NS _(a)_(b)) ))
        T(c, S(a, A::b, S(b, A::b::c, S(c, a, ALIAS(a, NS _(a)_(b)_(c)) )))
            T(d, S(a, A::b, S(b, A::b::c, S(c, A::b::c::d, S(d, a, ALIAS(a, NS _(a)_(b)_(c)_(d)) ))))
            ))))
};

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

16

Re: Наслідування шаблонів. template<typename T, typename N = Node<T, N>>

leofun01 написав:

понакидуйте трохи критики

Це не читабельне від слова взагалі :D

T(a, GENERATE_TYPE(a, a)
    T(b, S(a, A::b, S(b, a, ALIAS(a, NS _(a)_(b)) ))
        T(c, S(a, A::b, S(b, A::b::c, S(c, a, ALIAS(a, NS _(a)_(b)_(c)) )))
            T(d, S(a, A::b, S(b, A::b::c, S(c, A::b::c::d, S(d, a, ALIAS(a, NS _(a)_(b)_(c)_(d)) ))))
            ))))
Подякували: leofun011

17

Re: Наслідування шаблонів. template<typename T, typename N = Node<T, N>>

wander написав:

Це не читабельне від слова взагалі :D

Так краще ?

T(a,                                           GENERATE_TYPE(a,      a             )
T(b, S(a, A::b, S(b,                                a, ALIAS(a, NS _(a)_(b)        )   ))
T(c, S(a, A::b, S(b, A::b::c, S(c,                  a, ALIAS(a, NS _(a)_(b)_(c)    )  )))
T(d, S(a, A::b, S(b, A::b::c, S(c, A::b::c::d, S(d, a, ALIAS(a, NS _(a)_(b)_(c)_(d)) ))))
))))

18

Re: Наслідування шаблонів. template<typename T, typename N = Node<T, N>>

leofun01 написав:
wander написав:

Це не читабельне від слова взагалі :D

Так краще ?

T(a,                                           GENERATE_TYPE(a,      a             )
T(b, S(a, A::b, S(b,                                a, ALIAS(a, NS _(a)_(b)        )   ))
T(c, S(a, A::b, S(b, A::b::c, S(c,                  a, ALIAS(a, NS _(a)_(b)_(c)    )  )))
T(d, S(a, A::b, S(b, A::b::c, S(c, A::b::c::d, S(d, a, ALIAS(a, NS _(a)_(b)_(c)_(d)) ))))
))))

У мене ваш код не працює :)

Прихований текст
<source>:21:65: error: use of undeclared identifier 'nodes'
   21 | T(b, S(a, A::b, S(b,                                a, ALIAS(a, NS _(a)_(b)        )   ))
      |                                                                 ^
<source>:18:12: note: expanded from macro 'NS'
   18 | #define NS nodes
      |            ^
<source>:21:68: error: expected ';' after alias declaration
   21 | T(b, S(a, A::b, S(b,                                a, ALIAS(a, NS _(a)_(b)        )   ))
      |                                                                    ^
<source>:16:17: note: expanded from macro '_'
   16 | #define _(A) ::A<T_##A, N_##A>
      |                 ^
<source>:21:65: error: use of undeclared identifier 'nodes'
   21 | T(b, S(a, A::b, S(b,                                a, ALIAS(a, NS _(a)_(b)        )   ))
      |                                                                 ^
<source>:18:12: note: expanded from macro 'NS'
   18 | #define NS nodes
      |            ^
<source>:21:68: error: expected ';' after alias declaration
   21 | T(b, S(a, A::b, S(b,                                a, ALIAS(a, NS _(a)_(b)        )   ))
      |                                                                    ^
<source>:16:17: note: expanded from macro '_'
   16 | #define _(A) ::A<T_##A, N_##A>
      |                 ^
<source>:22:65: error: use of undeclared identifier 'nodes'
   22 | T(c, S(a, A::b, S(b, A::b::c, S(c,                  a, ALIAS(a, NS _(a)_(b)_(c)    )  )))
      |                                                                 ^
<source>:18:12: note: expanded from macro 'NS'
   18 | #define NS nodes
      |            ^
<source>:22:68: error: expected ';' after alias declaration
   22 | T(c, S(a, A::b, S(b, A::b::c, S(c,                  a, ALIAS(a, NS _(a)_(b)_(c)    )  )))
      |                                                                    ^
<source>:16:17: note: expanded from macro '_'
   16 | #define _(A) ::A<T_##A, N_##A>
      |                 ^
<source>:22:65: error: use of undeclared identifier 'nodes'
   22 | T(c, S(a, A::b, S(b, A::b::c, S(c,                  a, ALIAS(a, NS _(a)_(b)_(c)    )  )))
      |                                                                 ^
<source>:18:12: note: expanded from macro 'NS'
   18 | #define NS nodes
      |            ^
<source>:22:68: error: expected ';' after alias declaration
   22 | T(c, S(a, A::b, S(b, A::b::c, S(c,                  a, ALIAS(a, NS _(a)_(b)_(c)    )  )))
      |                                                                    ^
<source>:16:17: note: expanded from macro '_'
   16 | #define _(A) ::A<T_##A, N_##A>
      |                 ^
<source>:23:65: error: use of undeclared identifier 'nodes'
   23 | T(d, S(a, A::b, S(b, A::b::c, S(c, A::b::c::d, S(d, a, ALIAS(a, NS _(a)_(b)_(c)_(d)) ))))
      |                                                                 ^
<source>:18:12: note: expanded from macro 'NS'
   18 | #define NS nodes
      |            ^
<source>:23:68: error: expected ';' after alias declaration
   23 | T(d, S(a, A::b, S(b, A::b::c, S(c, A::b::c::d, S(d, a, ALIAS(a, NS _(a)_(b)_(c)_(d)) ))))
      |                                                                    ^
<source>:16:17: note: expanded from macro '_'
   16 | #define _(A) ::A<T_##A, N_##A>
      |                 ^
<source>:23:65: error: use of undeclared identifier 'nodes'
   23 | T(d, S(a, A::b, S(b, A::b::c, S(c, A::b::c::d, S(d, a, ALIAS(a, NS _(a)_(b)_(c)_(d)) ))))
      |                                                                 ^
<source>:18:12: note: expanded from macro 'NS'
   18 | #define NS nodes
      |            ^
<source>:23:68: error: expected ';' after alias declaration
   23 | T(d, S(a, A::b, S(b, A::b::c, S(c, A::b::c::d, S(d, a, ALIAS(a, NS _(a)_(b)_(c)_(d)) ))))
      |                                                                    ^
<source>:16:17: note: expanded from macro '_'
   16 | #define _(A) ::A<T_##A, N_##A>
      |                 ^
12 errors generated.
Compiler returned: 1

Clang 17.0.1 -std=c++20

UPD. Хибна тривога, все ок. Я недогледів.

Подякували: leofun011

19

Re: Наслідування шаблонів. template<typename T, typename N = Node<T, N>>

wander написав:

Clang 17.0.1 -std=c++20

https://i.ibb.co/tbkJrvL/cpp20-glasses.jpg

20 Востаннє редагувалося steamwater (28.02.2024 17:51:11)

Re: Наслідування шаблонів. template<typename T, typename N = Node<T, N>>

leofun01, це остання спроба якось з вами спiлкуватися. Ви наче не бачили моє повiдомлення, де я спитав про вашу цiль.Вона сформульована протиречно.
Неможливо в С++ створити контейнер що мiстить "довiльний" тип данних. Навiть якщо обмежити рiзноманiття "довiльний" iерархiєю наслiдування, то перекладати вiдповiдальнисть за тип данних на контейнер на рiвнi його комiрок погана iдея. Iтератори будуть повиннi вiдповiдати за надприроднi речi. Тому пiдтипiзувати треба тип данних, а не елемент контейнеру. Така моя думка.
Я запитав про N-дерева, доречi. То дерева з "довiльною" кiлькiстю гiлок на будьякому нодi. А тип у нодi може бути нащадком якогось загального iнтерфейсу. Якось так наприклад:

#include <iostream>
#include <typeinfo>
using std::cout;

// за умов пiдтипизацiї за рахунок наслiдування
class BaseDataInterface // без вiдкритого iнтерфейсу нi яка технiка type erasure не працюватиме
{
    public:
    virtual void base_data_operation() const = 0;
    virtual ~BaseDataInterface() {};
};
template<class T> class ConcreteData : public BaseDataInterface
{
    public:
    ConcreteData(const T &data) : m_data(data) {};
    // решта конструкторiв та операторiв
    void base_data_operation() const
    {
        cout<< typeid(m_data).name()<<'\n';
    }
    ~ConcreteData() = default;
    T m_data;
};
// тут можете писати контейнера якi мicтять вказiвник(и) (краще смарти) на BaseDataInterface

int main()
{
    BaseDataInterface
        *pint = new ConcreteData<int>(123),
        *pdbl = new ConcreteData<double>(1.23);

    pint->base_data_operation(); // i in G++
    pdbl->base_data_operation(); // d ...
    delete pint;
    delete pdbl;

    return 0;
}

але в цьому разi тип не "довiльний". А пiдтипи обмеженi рамками зазгелiдь опублiкованого iнтерфейсу.