Непрозрачный указатель на структуру с элементами шаблона
Предположим, я строю связанный список (реальная структура данных совершенно другая, но для вопроса достаточно связанного списка), чьи узлы выглядят как
template <typename T>
struct node
{
struct node<T> *next;
T data;
};
Для моей структуры данных у меня есть много функций с типом возврата struct node *
и я хочу, чтобы пользователь рассматривал этот тип как непрозрачный. В примере со связанным списком такой функцией может быть, например, get_next(struct node<T> *n)
или же insert_after(struct node<T> *x, struct node<T> *y)
, Только очень мало функций, а именно те, которые выделяют node
s или получить / установить их data
поле, нужно знать что-нибудь о T
,
Есть ли лучший способ "игнорировать" T
"и позволить пользователю взаимодействовать только с чем-то вроде typedef struct node * opaque_handle
для тех функций, которые никогда не должны заботиться о T
? Моя внутренняя реакция, исходящая от C, состоит в том, чтобы бросать в и из void*
, но это звучит не очень элегантно.
Изменить: комментарий CygnusX1 убедил меня, что я прошу слишком много гарантий от системы типов в то же время, что я пытаюсь обойти слишком много из этих гарантий. Я вернусь к сдаче T
быть void *
за счет литья и косвенного обращения.
2 ответа
Пока тебя не волнует, что T
вы больше всего хотите отличить его от другого типа - скажем, U
не так ли? Вы, вероятно, хотите, чтобы следующее вызвало ошибку:
node<T>* elem1 = ...
node<U>* elem2 = ...
elem1 = elem2
Есть несколько способов сделать ваш код проще, не жертвуя проверкой типов или производительностью во время выполнения:
- Если вы используете C++11, подумайте об использовании
auto
вместо того, чтобы явно называть тип при использовании ваших функций - Если
node<T>
очень часто встречается в вашем коде, вы можете установить глобальную область видимостиtypedef
Также обратите внимание, что в контексте node<T>
определение, используя равнину node
(без аргументов шаблона) допускается.
Если вы действительно хотите скрыть содержимое node
рассмотрите возможность реализации шаблона pimpl, предложенного mvidelgauz.
Если вы можете использовать boost, тогда boost::any или boost:: option может помочь реализовать гетерогенные контейнеры.
Это то, что вы ищете?
#include <iostream>
#include <boost/any.hpp>
#include <list>
using Collection = std::list<boost::any>;
using Node = Collection::iterator;
static Collection anys;
template<typename T>
Node insert_after(T const& obj, Node start_pos = anys.end())
{
return anys.insert(start_pos, boost::any(obj));
}
void print_type(boost::any const& a)
{
if (a.type() == typeid(int)) { std::cout << "int" << std::endl; }
else if (a.type() == typeid(float)) { std::cout << "float" << std::endl; }
}
int main()
{
const auto node1 = insert_after(int(1));
const auto node2 = insert_after(float(2.57));
const auto node3 = insert_after(int(3));
std::cout << boost::any_cast<int>(*node1) << std::endl;
std::cout << boost::any_cast<float>(*node2) << std::endl;
std::cout << boost::any_cast<int>(*node3) << std::endl;
print_type(*node1);
print_type(*node2);
print_type(*node3);
return 0;
}
Выходы:
1
2.57
3
int
float
int