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, 53 downloads since 2016-05-16 

Подякували: 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;
}
life is too short to remove usb safely
Подякували: koala, leofun01, ReAl3

4 Востаннє редагувалося -=ЮрА=- (16.05.2016 20: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.
Це звичайно не те що мені було потрібно, але частково допомогло.