【问题标题】:std::map::size_type for a std::map whose value_type is its own size_typestd::map::size_type 用于其 value_type 是它自己的 size_type 的 std::map
【发布时间】:2019-04-25 03:45:41
【问题描述】:

我有一个std::map<std::pair<std::string, std::string>, float>,它占用了太多内存,为了使用更少的内存,我决定将唯一字符串映射到整数(例如,std::map<std::string, int>,其中每个新的唯一字符串都被映射映射到当前的size()),并将这些整数值用作映射的成对键(例如,std::map<std::pair<int, int>, float>)。

我想用std::map::size_type代替int

using map_index = std::map::size_type;
std::pair<map_index, map_index> key;

当然,这不会编译,因为我需要为地图提供参数列表:

vector.cc:14:19: error: invalid use of template-name `std::map' without an argument list
 using map_index = std::map::size_type;

这(理论上)是我想要实现的目标:

using map_index = std::map<std::string, map_index>::size_type;

它给出了以下(预期的)编译器错误:

vector.cc:15:41: error: `map_index' was not declared in this scope
 using map_index = std::map<std::string, map_index>::size_type;

让编译器为 std::map 推断正确的 value_type 的正确方法是什么,而 value_type 是它自己的 size_type

【问题讨论】:

  • 次要吹毛求疵:我认为你的最后一句话是错误的。要知道size_type 是什么,您首先需要知道value_type 是什么,而不是相反。一旦你知道地图的类型,得到它的size_type 就很简单了
  • @user463035818 问题似乎是size_type 是OP 的value_type 的一部分。
  • 你有一个循环依赖。为什么你不能使用size_t(这通常是size_type)?
  • 你能解释一下你为什么想要那个吗? afaik map::size_type 只是一个 typedef 别名无论如何总是相同的类型
  • std::map&lt;K, V&gt;::size_type最有可能完全独立于KV。如果你真的在意,可以static_assert(std::is_same_v&lt;Map::size_type, Map::mapped_type&gt;, "Unexpected size_type")

标签: c++ c++11 stl stdmap size-type


【解决方案1】:

size_t 应该足以应付这种情况。

但如果你坚持,你可以这样做:

#include <type_traits>
#include <map>

template <class Key, class Value = size_t, size_t depth = 0, class = void>
struct GetSizeType {
    using type = typename GetSizeType<Key, typename std::map<Key, Value>::size_type, depth + 1>::type;
};

template <class Key, class Value, size_t depth>
struct GetSizeType<Key, Value, depth, std::enable_if_t<std::is_same_v<Value, typename std::map<Key, Value>::size_type>>> {
    using type = typename std::map<Key, Value>::size_type;
};

template <class Key, class Value>
struct GetSizeType<Key, Value, 100, void> {};

int main() {
    using X = GetSizeType<int>::type;

    return 0;
}

它将在GetSizeType上递归运行,递归调用将停止

  • 达到递归调用深度限制(在这种情况下将没有成员type),或者
  • 找到std::map 的特化,其中mapped_typesize_type 相同(成员type 别名size_type)。

【讨论】:

  • 一阶的技术暴徒方法 - 太棒了!
  • 我不完全同意“你会在用完 size_t 之前用完内存。”。像my_map.size() * 2; 这样的操作是一个明智的操作,它可以在内存不足之前很久就溢出
  • @user463035818 谢谢,我已经删除了那部分。我错误地简化了用法。
  • @user463035818 - 在问题设置的上下文中,不涉及算术 - size() 的结果只是被用作不透明的标识符。
  • @vallismortis “更极端的情况”是什么意思?如果你使用std::size_t,它不可能在其他类型的地方不起作用,因为它应该能够处理大小为 1 的对象。
【解决方案2】:

免责声明:这个解决方案非常愚蠢。我们将通过重复(通常一次)尝试实例化 std::map 来解决等式,直到我们找到一个具有请求的键和它自己的 size_type 作为值的那个。

template <class T>
struct identity {
    using type = T;
};

template <class K, class V = char>
struct auto_map {
    using map_type = std::map<K, V>;
    using type = typename std::conditional_t<
        std::is_same_v<
            typename map_type::mapped_type,
            typename map_type::size_type
        >,
        identity<map_type>,
        auto_map<K, typename map_type::size_type>
    >::type;
};

template <class K>
using auto_map_t = typename auto_map<K>::type;

如果元函数找不到这样的映射,它要么会因为type 最终定义给自己而出错,要么会打破递归限制。

【讨论】:

  • 显示的两种递归解决方案都很酷,但我不确定为什么要使用它。正如您所说,它仍然不能保证您会找到在损坏的环境中工作的一个,因此编译以简单地选择一个并断言我们实际需要的条件会更干净,更快捷,不是吗?
  • @Acorn 是的。在进行全面重建时,我只是需要做一些事情;)
【解决方案3】:

使用std::size_t。无符号整数 std::map::size_type 不会大于 std::size_t 并且在实践中将是相同的类型。

如果你想确定,就断言它:

static_assert(std::is_same_v<
    std::size_t,
    std::map<std::string, std::size_t>::size_type
>);

【讨论】:

    【解决方案4】:

    我使用的所有 C++ 实现对所有地图都使用相同的大小类型。

    所以;

    using map_size_type = std::map<int, int>::size_type;
    using my_map = std::map<std::string, map_size_type>;
    static_assert(std::is_same<map_size_type, my_map::size_type);
    

    如果(合理的)假设失败,这只会强制编译错误。

    【讨论】:

    • 我在这里链接another question,因为您的回答似乎也与之相关。
    【解决方案5】:

    一般来说,您要寻找的东西是不可能的。

    可以想象(尽管有些牵强)std::map&lt;int, long&gt;::size_typeintstd::map&lt;int, int&gt;::size_typelong(对于其他整数类型也是如此),在这种情况下,没有可能的方法来满足 std::map&lt;int, T&gt;::size_typeT.

    相反,对于所有Tstd::map&lt;int, T&gt;::size_type 可能被定义为T,在这种情况下,没有唯一的T 满足您的“要求”。

    正如几个答案(以及您自己的参考链接)所提到的,实际上它不太可能是 size_t

    【讨论】:

    • 这是我一直在寻找的答案,只是不是我所希望的。您提供的具体示例完美地说明了问题。
    【解决方案6】:

    打破循环依赖的唯一方法是使用特定类型。我建议您简单地将map_index 设为std::size_t - C++ 强impliesstd::size_t 将可分配给map::size_type

    【讨论】:

      【解决方案7】:

      但是您确定std::mapsize_type 取决于键/值类型吗?

      如果是这样,我没有办法得到它。

      size_type 不应该依赖于键/值类型,通常是std::size_t

      我建议

      using Index0 = typename std::map<std::string, std::size_t>::size_type;
      
      using mapIndex = typename std::map<std::string, Index0>::size_type;    
      

      你可以检查你是否得到了正确的类型

      static_assert( std::is_same_v<Index0, mapIndex>, "no right type");
      

      【讨论】:

      • 编译器无法知道地图的大小类型始终相同 - 就它而言,可能存在不同的专业化。
      • @TobySpeight 是的,这正是促使我提出这个问题的原因。正是 C++ map documentation 中的“通常与 size_t 相同”子句真正让我想到了这一点。
      • @vallismortis - 从理论的角度来看,你是对的(据我所知)。我没有看到打破循环依赖的方法,但添加 static_assert( std::is_same_v&lt;Index0, mapIndex&gt;, "no right type"); 您可以检查所选类型是否正确。
      • @TobySpeight - 你是对的(据我所知);但是通过static_assert()(见我修改后的答案)我们可以检查我们是否得到了正确的类型。
      猜你喜欢
      • 2017-12-23
      • 2018-02-15
      • 2012-10-12
      • 2012-09-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-24
      • 2019-08-08
      相关资源
      最近更新 更多