Шаблонные рекурсивные типы данных

У меня есть рекурсивный тип данных, например:

template<typename T>
struct SomeType {
    std::map<T, SomeType<T>> mapping;
};

SomeType<int> foo;

Это отлично работает, но замена std::map на std::unordered_map приводит к ошибке компиляции из-за неполного типа. Я где-то ошибаюсь (или gcc)? или это просто часть стандарта?

Я бы также хотел, чтобы внутренний контейнер определялся параметром шаблона (например, std::stack и std::queue), но я не могу понять, как это сделать, поскольку для этого потребуется определить SomeType.

Неполный пример:

template<typename T, typename C = std::map<T, SomeType<[???]>>>
struct SomeType {
    C mapping;
};

SomeType<int, [???]> foo;

Я знаю, что это можно сделать с помощью runtime-косвенности, но это не то, что я ищу.

Ответ 1

Ваш класс является неполным до конечного } его определения. Таким образом, член mapping использует неполный тип SomeType в аргументах шаблона типа.

Стандарт не позволяет этого, и это просто удача, что он работает с некоторыми контейнерами STL.

Ваши второстепенные вопросы подпадают под один и тот же ответ - в первую очередь это незаконно.

Ответ 2

Вы не можете определить шаблон с рекурсивными параметрами по очевидным причинам. Вы также не можете создавать шаблоны контейнеров стандартной библиотеки на неполных типах, поскольку стандарт говорит об этом (в противном случае это поведение undefined). Обычная идиома PIMPL может помочь, хотя:

#include <map>
#include <memory>
template <typename T> class SomeType
{
    typedef std::map<T, SomeType<T>> map_type;
    typedef std::unique_ptr<map_type> map_ptr;
    map_ptr pimpl;
public:
    SomeType() : pimpl(new map_type) { }
};

Ответ 3

Пока вы не можете использовать неполные типы с контейнерами, вы можете сделать это с помощью интеллектуальных указателей. И хотя вы не можете создавать типы шаблонов с параметрами undefined types, вы можете использовать некоторые трюки здесь:

template<typename T, template <typename U, typename V, typename... Args> class Container = std::unordered_map >
struct SomeType {
    Container<T, std::unique_ptr<SomeType> > mapping;
};