У меня есть общий класс кэширования (пример упрощен по сравнению с тем, что есть в моей кодовой базе). Элементы хранятся на карте, а тип шаблона T должен иметь поле id
, чтобы я мог сохранить его на карте.
struct Car
{
int id;
std::string name;
};
struct Aeroplane
{
std::string id;
int top_speed;
std::string brand;
};
template<class T, class S>
class Cache
{
public:
void Add(T item)
{
cache.emplace(item.id, item);
}
bool Contains(T item)
{
return cache.find(item.id) != cache.end();
}
private:
map<S, T> cache;
};
int main()
{
Cache<Car, int> cars;
// we can add Car instances to cars here
Cache<Aeroplane, std::string> aeroplanes;
// we can add Aeroplane instances to aeroplanes here
}
На данный момент класс S может быть любым, но он должен быть того же типа, что и T.id. Было бы очень хорошо, если бы мы могли сказать, что кеш должен быть картой из типа T.id в T. Тогда я могу упростить шаблонизацию, удалив класс S.
template<class T>
class Cache
{
public:
... should be same as above ...
private:
// is it possible?
map< /* the type of T.id ??? */ , T> cache;
};
int main()
{
// it would be very nice if we can simplify it, so that the definition looks like this
Cache<Car> cars;
// and...
Cache<Aeroplane> aeroplanes;
}
Кто-нибудь знает, как мне это сделать?
До C++11 вы могли заставить класс иметь typedef: typedef int id_type;
.
Вы можете использовать decltype, чтобы получить тип выражения. И выражение на самом деле не будет выполняться, а просто оцениваться с точки зрения типа.
Таким образом, вы можете сделать что-то вроде
template<class T, class S = decltype(T().id)>
class Cache
{
...
}
Это гарантирует, что S
имеет тот же тип, что и id
в экземпляре T
.
Вы можете указать значение по умолчанию для S
следующим образом:
template<class T, class S = decltype(std::declval<T>().id)>
class Cache
{
public:
void Add(T item)
{
cache.emplace(item.id, item);
}
bool Contains(T item)
{
return cache.find(item.id) != cache.end();
}
private:
map<S, T> cache;
};
int main()
{
Cache<Car> cars;
// we can add Car instances to cars here
Cache<Aeroplane> aeroplanes;
// we can add Aeroplane instances to aeroplanes here
}
Или избавьтесь от второго параметра шаблона, например:
template<class T>
class Cache
{
public:
using S = decltype(std::declval<T>().id);
// ...
map<S, T> cache;
};
Или короче (спасибо @MikeVine):
template<class T>
class Cache
{
public:
using S = decltype(T::id);
// ...
map<S, T> cache;
};
В чем преимущество decltype(std::declval<T>().id)
перед decltype(T::id)
?
@MikeVine нет. Разница между ними в том, что я не знал, что decltype(T::id)
компилируется;) Я добавлю это к ответу. Спасибо
Я запускаю его с помощью g++, и первый и второй варианты прекрасно работают. Моему g++ не нравится третий. Но этого достаточно для меня. Спасибо.
Без проблем скомпилировать все параметры с помощью g++ с языковой версией C++17.
decltype(T::id)
?