Предположим, я строю связанный список (реальная структура данных совершенно другая, но для вопроса достаточно связанного списка), чьи узлы выглядят как
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 *
за счет литья и косвенного обращения.
Пока тебя не волнует, что T
вы больше всего хотите отличить его от другого типа — скажем, U
не так ли?
Вы, вероятно, хотите, чтобы следующее вызвало ошибку:
node<T>* elem1 = ...
node<U>* elem2 = ...
elem1 = elem2
Есть несколько способов сделать ваш код проще, не жертвуя проверкой типов или производительностью во время выполнения:
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