【问题标题】:associative std::tuple container关联 std::tuple 容器
【发布时间】:2015-03-10 08:53:32
【问题描述】:

是否可以定义(以简单的方式,可能重复使用 std 容器)“关联 std::tuple”,或者换句话说,“变量 std::map”。

类似这样的(这个界面只是说明,欢迎其他可能的界面):

AssociativeTuple<std::string> at;    // std:string is the key type
at.insert<float>("my_float", 3.14);  // 1.
at.insert<int>("my_int", 42);
at.insert<bool>("my_bool", true);
at.insert<int>("xyz", 0);
at.insert<std::string>("my_string", "hello world!");

assert(get(at, "my_float") == 3.14);  // 2.
assert(get(at, "my_int") == 42);
assert(at["my_string"] == "hello world!");  // 3.

assert(std::is_same<at.type_of("my_float")::type, float>)  // 4.

for (auto it : at) { std::cout << it.first << " = " << it.second; }  // 5.

其他理想的约束:

  1. 值/键集仅在运行时已知。但是在编译时,用户知道(值的类型)和键之间的关系。例如,用户在编译时知道"my_float" 将是float。换句话说,可能的键集合是固定的,并且对应于键的值的类型在编译时是已知的。编译时不知道的是“是否”将密钥插入容器内。当然,映射值的值在编译时是未知的。
  2. 访问性能,get 应该很快
  3. 用户不必记住与键关联的类型

我真正的问题只是float/int/bool 类型的值(我正在做的是将所有内容存储在std::map&lt;std::string, float&gt; 中并在必要时转换为int),但是一个通用的解决方案是可取的。在我的真实情况下,密钥总是std::string

【问题讨论】:

  • std::map&lt;std::string, variant&gt; 不能解决您的问题吗?
  • @JBL: 第4行怎么办?
  • 我已经更好地解释了编译时已知的内容
  • 我们在谈论多少个不同的键?
  • 您对第 1 点的编辑是否与第 3 点相矛盾?

标签: c++ c++11 containers variadic-templates


【解决方案1】:

您可能是指具有多态值的地图。对于多态值,您可以使用boost::any,或者更好的是boost::variant&lt;&gt;。例如:

typedef boost::variant<int, double, std::string> MyVariant;
typedef std::map<std::string, MyVariant> MyPolymorphicMap;

【讨论】:

  • 嗯,“用户不必记住与键关联的类型”使得带有any 的版本不可能,对吧?
  • @JBL 我不会这么说的。您可以从boost::any 获取类型信息,只是类型信息本身并不是很有用。实际上,boost::variant&lt;&gt; 更有用。
  • 您声明 MyVariant 是一个带有三个模板参数的 boost::variant。为什么只有 3 而不是无限的?我最好重写编译时已知的内容。
  • @RuggeroTurra 在您最初的问题中,您只有三种类型的值:intdoublestd::string。您应该能够更改定义以包含您需要的任何类型。 (显然这里放不下无限列表,因为这个网站上没有无限存储这样的列表),
【解决方案2】:

这个怎么样? (注意:这些值不是元组;每个键都有一个值)。

template<class K> using AnyMap = std::map<K, boost::any>;

AnyMap map;
map["test1"] = 124;
map["test2"] = std::string{ "some text" };

auto value = boost::any_cast<int>(map["test1"]);

【讨论】:

  • 是否可以满足3. The user don't have to remember the type associated to a key的要求?我指的是boost:any_cast&lt;int&gt;
  • 是与否; boost::any 有一个存储类型的访问器,但这会返回一个 std::typeid(这是非常有限的)。因此,boost::variant 更加灵活(您可以在variant 上执行比any 更多的操作)。
【解决方案3】:

是的,这可以使用变体(那里有很多实现)或一般某种包装器,或者基于联合(在您的情况下是首选方式)或从公共基类派生。

对于像 get(at, "my_int") == 42 这样的事情,您必须重载相等运算符 bool operator==(Variant&amp;, int),但这也不成问题。

你不能做像 std::is_same&lt;at.type_of("my_float")::type, float&gt; 这样的事情,因为 is_same 是一个编译时表达式,但类型(你正在寻找的)只有在运行时才知道。但是,您仍然可以定义执行该检查的运行时函数。

但是:如果 - 在您的特定情况下 - 您只需要 intfloat 并且您没有难以记忆(使用字符串作为键似乎表明这一点),那么我只使用了 @987654327 @ 因为它可以代表您可能想要存储的任何数字。

另一种可能性是使用两个独立的数据结构(一个用于 int,一个用于 float)。如有必要,您还可以围绕它们构建一个 rwarpper,以使它们看起来像一个单独的。如果您的程序逻辑在执行查找之前总是知道与键关联的类型,那么使用多个数据结构可能特别有意义。

【讨论】:

  • 请不要推荐使用double 来替代存储所有类型的数字。 double(和float)以浮点形式存储,其中looses precision 的计算结果最多。另一件事:您不需要重载相等运算符来将get(variant_type, key) 的结果与数字进行比较。
  • @utnapistim:为什么不超载?此外,由于 double 的精度约为 15 个十进制数字,而 int 仅约为 10,因此每个整数和每个浮点数都可以用 double 精确表示
  • “准确”打击,我的意思是没有“准确性损失”
  • 64 位编译怎么样(64 位整数不能表示为双精度 - 当您使用某些整数类型时,这会变得很棘手,例如 std::size_t)?关于重载,get 函数将返回值,所以最后,您将比较两个整数(不需要使用 Variant&amp; 重载)。
  • @utnapistim:我说的是int,而不是std::size_t。虽然标准不能保证,但在我所知道的几乎所有“相关”平台(尤其是 x64)上,sizeof(int) 是 4。在编译时,一般的 get 函数应该如何返回 int 或 double,它只知道,它会访问一个变体?据我所知,这只有在您获得模板函数并直接指定类型时才有效,例如get&lt;init&gt;(),这不是被问到的。
猜你喜欢
  • 1970-01-01
  • 2019-06-15
  • 1970-01-01
  • 1970-01-01
  • 2016-11-11
  • 2011-10-19
  • 1970-01-01
  • 1970-01-01
  • 2019-08-19
相关资源
最近更新 更多