[英]How to use variadic templates as keys to a map of std::type_index?
[英]Using templates as keys in a std::map
我想知道是否可以使用模板作為地圖的關鍵。 例如:
std::map< <T> , Node> nodes;
本質上,我想要做的是能夠擁有一堆包含任意類型數據的節點,並且由該數據鍵入。 我想我可以通過將所述數據轉換為二進制並按鍵進行鍵控來實現,但這很麻煩,我想避免它。
為了澄清,我希望能夠使用任何類型的變量作為鍵。 例如,如果我有2個節點,一個包含int作為其數據,另一個包含Foo作為其數據,我希望能夠使用它們的數據作為鍵將它們放在同一個映射中。 也許地圖不是我想要的,我不確定......
思考? 謝謝!
我認為為此目的,使用std::type_info
更容易:
std::map<std::type_info, std::string> m;
m[typeid(int)] = "integer";
但這實際上取決於你想要達到的目標,我現在還不清楚。 希望有所幫助
我通常做的是:
template<typename T>
void type_id(){}
using type_id_t = void(*)();
然后,我像這樣使用它:
std::map<type_id_t, Node> nodes;
nodes[type_id<AType>] = Node{...};
nodes[type_id<BType>] = Node{...};
當然,這可以通過C ++ 14中的變量模板來增強。
對不起,我剛剛重新閱讀了這個問題,我對它的了解更多。
你想要的是std::any
,但它只是C ++ 17。 你可以使用boost::any
。
它看起來像這樣:
std::map<std::any, Node> nodes;
nodes.emplace("string as key", Node{});
nodes.emplace(23, Node{});
只要地圖可以某種方式訂購std::any
實例,它就應該工作。 如果沒有,您可以改為使用哈希映射:
std::unordered_map<std::any, Node> nodes;
只要地圖可以散列任何一個,它就會起作用。
至少如果我理解你想要什么,簡短的回答是否定的。
必須對地圖中的鍵進行排序 - 也就是說,必須為任何一對鍵A和B定義,您必須定義一個有序關系,其中A小於B,或B小於A,或者兩個鍵是等價的。
給定兩個完全任意類型的鍵,不會有一種比較它們的定義方式。 因此,您不能將它們用作地圖的鍵。
為了得到某種結果,您需要定義一些您想要支持的特定類型。 然后你可以定義一些(大致)你想要支持的所有類型的聯合。 僅憑這一點還不夠 - 你還必須定義訂購。 根據您要完成的任務,您可能(例如)在每個對象中都有一個ID,並按ID排序。 或者,您可以定義對象之間的順序,因此(例如)每個Person
在每個Dog
之前排序,在每個Dream
之前排序,依此類推。 然后你必須像往常一樣在每種類型中定義一個排序。
然而,我警告說,這通常涉及相當多的額外工作,並且提供的回報非常小。 我會說超過90%的時間我見過人們(嘗試)做到這一點,這是一個錯誤,最好的效果不佳。 如果可能的話,我會嘗試找到其他方法來解決你想要解決的任何問題。
您可以創建一個類似於poly_key
的類,它將接受任何類型,只要它是:
可復制或可移動(可演示此版本)
平等可比(如果在無序地圖中使用)
低於可比性(如果在地圖中使用)
可清洗的(如果在有序地圖中使用)
ostreamable(本演示版)
像這樣:
#include <iostream>
#include <string>
#include <memory>
#include <unordered_map>
#include <map>
#include <typeinfo>
/// a non-polymorphic container for a polymorphic key type
struct poly_key
{
/// concept defines the key's capabilities
struct concept {
virtual bool equal(const void* other) = 0;
virtual bool less(const void* other) = 0;
virtual const void* address() const = 0;
virtual const std::type_info& type() const = 0;
virtual std::size_t hash() const = 0;
virtual std::ostream& emit(std::ostream&) const = 0;
virtual std::unique_ptr<concept> clone() const = 0;
virtual ~concept() = default;
};
using ptr_type = std::unique_ptr<concept>;
/// model<> models the concept for any key which supports the required operations
template<class T>
struct model : concept {
model(T&& t) : _t(std::move(t)) {}
bool equal(const void* other) override {
return _t == (*reinterpret_cast<const T*>(other));
}
bool less(const void* other) override {
return _t < (*reinterpret_cast<const T*>(other));
}
const void* address() const override {
return std::addressof(_t);
}
const std::type_info& type() const override {
return typeid(_t);
}
std::size_t hash() const override {
return std::hash<T>()(_t);
}
std::ostream& emit(std::ostream& os) const override
{
return os << _t;
}
virtual std::unique_ptr<concept> clone() const override
{
return std::make_unique<model>(*this);
}
T _t;
};
template<class T>
poly_key(T t) : _impl(std::make_unique<model<T>>(std::move(t))) {}
std::size_t hash() const {
return _impl->hash();
}
bool operator==(const poly_key& r) const {
return _impl->type() == r._impl->type()
&& _impl->equal(r._impl->address());
}
bool operator<(const poly_key& r) const {
auto& lt = _impl->type();
auto& rt = r._impl->type();
if (lt.before(rt)) {
return true;
}
else if (rt.before(lt)) {
return false;
}
else {
return _impl->less(r._impl->address());
}
}
poly_key(const poly_key& r)
: _impl(r._impl->clone())
{
}
poly_key(poly_key&& r)
: _impl(std::move(r._impl))
{
}
friend std::ostream& operator<<(std::ostream& os, const poly_key& k)
{
return k._impl->emit(os);
}
ptr_type _impl;
};
/// make it hashable
namespace std {
template<> struct hash<::poly_key> {
bool operator()(const ::poly_key& r) const {
return r.hash();
}
};
}
//
// test
//
int main()
{
std::unordered_map<poly_key, std::string> m;
m.emplace(poly_key(std::string("key 1")), "Hello");
m.emplace(poly_key(2), "World");
std::cout << "unordered:\n";
for (auto& e : m) {
std::cout << e.first << " : " << e.second << std::endl;
}
std::cout << "\nordered:\n";
std::map<poly_key, std::string> m2 (m.begin(), m.end());
for (auto& e : m2) {
std::cout << e.first << " : " << e.second << std::endl;
}
}
示例輸出(順序可能因工具集而異):
unordered:
2 : World
key 1 : Hello
ordered:
key 1 : Hello
2 : World
為您的類型命名並將其與地圖一起使用:
#include<map>
#include<cassert>
struct B { static int cnt; };
int B::cnt = 0;
template<typename T>
struct D: B { static const int type; };
template<typename T>
const int D<T>::type = B::cnt++;
std::map<int, int> values;
template<typename T>
void set(int value) { values[D<T>::type] = value; }
template<typename T>
int get() { return values[D<T>::type]; }
struct T1 { };
struct T2 { };
int main() {
set<T1>(42);
set<T2>(0);
assert(get<T1>() == 42);
assert(get<T2>() == 0);
set<T2>(3);
assert(get<T2>() == 3);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.