我正在尝试在 C++17 中创建一个字典,它将名称同时关联到任意数量类型的指针,并且能够在查找时推导出类型,而无需在调用方进行显式强制转换。这些类型无法在可变参数模板中预先指定。
所需的界面如下:
AnyDict<std::string, std::map> dict;
// If both inserts succeed this should evaluate to true.
if ( dict.insert(std::string {"Hello"}, 0.5f) &&
dict.insert(std::string {"Other"}, std::string {"Value"}) )
; // ...
// This should return true.
dict.remove(std::string {"Hello"});
// This should return "Other", but if
// something goes wrong returns "".
auto value = dict.find(std::string {"Other"}, std::string {""});
目前我最接近的解决方案包括:
// Currently I'm not using dlls or anything, but...
class TypeIdx
{
public:
template <class Type>
static int get()
{
// ugly.
static const int s_idx = s_count++;
return s_idx;
}
private:
static inline int s_count = 0;
};
template <template <class, class> class Dict>
class AnyDict
{
public:
// constructors, destructor, etc.
template <class Type>
bool
insert(Type& item)
{
int key = TypeIdx::get<Type>();
// The delegate must return true on success
// or false on failure.
return m_dict.insert(key, (void*) &item);
}
template <class Type>
bool
remove()
{
int key = TypeIdx::get<Type>();
// The delegate must return true on success
// or false on failure.
return m_dict.remove(key);
}
template <class Type>
Type*
find(Type* def)
{
int key = TypeIdx::get<Type>();
// The delegate must return a pointer to
// the item found on success, or to "def"
// on failure.
return (Type*) m_dict.find(key, def);
}
private:
// Here the void* lets me save a pointer to
// anything but i can't deduce back the type.
Dict<int, void*> m_dict;
};
虽然这可以工作并返回正确类型的指针而无需强制转换,但它仍然不会按名称而是按类型对它们进行索引,这不是我想要的。
你知道有什么解决办法吗?也许使用
std::any
会改善这种情况?另外,如果您能为此类建议一个更好的名称,我们将不胜感激。
我认为您应该使用
std::any
作为映射的值类型,因为它完全实现了您需要的类型擦除。无需担心可怕的空指针。这是一个帮助您入门的解决方案:
#include <any>
#include <string>
#include <map>
#include <iostream>
template <typename key_type, template<typename, typename> typename Map>
class AnyDict
{
public:
AnyDict() = default;
template <typename value_type>
bool insert(key_type const& k, value_type const& v) {
auto [it, rt] = m_values.insert(std::make_pair(k, std::any{v}));
return rt;
}
template <typename value_type>
value_type find(key_type const& k, value_type const& default_value) const
{
if (auto it = m_values.find(k); it != m_values.end()) {
if (it->second.type() != typeid(value_type)){
throw std::logic_error("type mismatch");
}
return std::any_cast<value_type>(it->second);
}
else {
return default_value;
}
}
bool remove(key_type const& k) {
if (auto it = m_values.find(k); it != m_values.end()) {
m_values.erase(k);
return true;
}
return false;
}
private:
Map<key_type, std::any> m_values;
};
int main()
{
AnyDict<std::string, std::map> dict;
// If both inserts succeed this should evaluate to true.
if ( dict.insert(std::string {"Hello"}, 0.5f) &&
dict.insert(std::string {"Other"}, std::string {"Value"}) )
{
std::cout << "Success.\n";
}
// This should return true.
std::cout << std::boolalpha << dict.remove(std::string {"Hello"}) << "\n";
// This should return "Other", but if
// something goes wrong returns "".
auto value = dict.find(std::string {"Other"}, std::string {""});
std::cout << value << "\n";
};
https://godbolt.org/z/GsrTE7975
现在我认为您想要的界面和您的问题之间存在一些细微的差异。其一,所需的接口不存储任何指针,而是存储值,也就是说,根据接口,
dict
拥有这些值。上面的代码允许您存储任何类型,包括,但不限于指针:https://godbolt.org/z/sfhT68xK9