c++如何理解map对象的value_type是pair类型

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++如何理解map对象的value_type是pair类型相关的知识,希望对你有一定的参考价值。

map<K, V>::value_type,它是一个pair类型,如何理解????象map的键值对的键对应的值如果为1,value_type是不是就为1,而pair包含两个数据值,且有可能为不同类型,那么value_type该怎么理解??谢谢

map 是以 pair形式插入的。

map中的元素的类型value_type
typedef pair<const Key, Type> value_type;
value_type 被声明为 pair <const key_type, mapped_type> 但并不是简单的 pair <key_type, mapped_type> 因为用一个非常量的迭代器或引用不能改变关联容器的Key。

#include <map>
#include <iostream>
int main( )

using namespace std;
typedef pair <const int, int> cInt2Int;
map <int, int> m1;
map <int, int> :: key_type key1;
map <int, int> :: mapped_type mapped1;
map <int, int> :: value_type value1;
map <int, int> :: iterator pIter;
// value_type can be used to pass the correct type
// explicitly to avoid implicit type conversion
m1.insert ( map <int, int> :: value_type ( 1, 10 ) );
// Compare other ways to insert objects into a map
m1.insert ( cInt2Int ( 2, 20 ) );
m1[ 3 ] = 30;
// Initializing key1 and mapped1
key1 = ( m1.begin( ) -> first );
mapped1 = ( m1.begin( ) -> second );
cout << "The key of first element in the map is "
<< key1 << "." << endl;
cout << "The data value of first element in the map is "
<< mapped1 << "." << endl;
// The following line would cause an error because
// the value_type is not assignable
// value1 = cInt2Int ( 4, 40 );
cout << "The keys of the mapped elements are:";
for ( pIter = m1.begin( ) ; pIter != m1.end( ) ; pIter++ )
cout << " " << pIter -> first;
cout << "." << endl;
cout << "The values of the mapped elements are:";
for ( pIter = m1.begin( ) ; pIter != m1.end( ) ; pIter++ )
cout << " " << pIter -> second;
cout << "." << endl;

Output
The key of first element in the map is 1.
The data value of first element in the map is 10.
The keys of the mapped elements are: 1 2 3.
The values of the mapped elements are: 10 20 30.
参考技术A map<string, int>::key_type v1; //string
map<string, int>::mapped_type v2; //int
map<string, int>::value_type v3; //pair<const string, int>
对于泛型编程来说,可能不知道具体类型是什么,所有就有了key_type、mapped_type、value_type这些类型。
key_type就表示map中key的类型。以下两句是一个意思:
map<string, int>::key_type v1;
string v1;
同理,mapped_type表示map中value的类型。

value_type这是具体值的类型,由于map中存的是键值对pair。而key又不能修改。
故为pair<const string,int>。

std::map::size_type 用于其 value_type 是它自己的 size_type 的 std::map

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

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

我想用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") 【参考方案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++ 强implies,std::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()(见我修改后的答案)我们可以检查我们是否得到了正确的类型。

以上是关于c++如何理解map对象的value_type是pair类型的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 地图中插入 vs emplace vs operator[]

std::map::size_type 用于其 value_type 是它自己的 size_type 的 std::map

如何向Map中添加数据

如何修改unordered_map中的值?

C++标准库之vector(各函数及其使用全)

Go语言中映射表map的使用