C++ STL——hash/unordered_set/c++11关键字decltype

本文主要介绍了C++ STL中的hash类模板和unordered_set类模板,包括它们的定义、作用以及如何使用。通过示例代码展示了如何创建和操作unordered_set,并解释了hash函数的作用和find()、equal_range()等方法的功能。同时,文中还探讨了C++11关键字decltype在函数模板中的应用。

摘自MSDN,以VS2012版为主


1、hash类模板

定义如下:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. template<class Ty>  
  2.     struct hash  
  3.         : public unary_function<Ty, size_t> {  
  4.     size_t operator()(Ty _Val) const;  
  5.     };  

并给出一个例子:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. // std_tr1__functional__hash.cpp   
  2. // compile with: /EHsc   
  3. #include <functional>   
  4. #include <iostream>   
  5. #include <unordered_set>   
  6.    
  7. int main()   
  8.     {   
  9.     std::unordered_set<int, std::hash<int> > c0;   
  10.     c0.insert(3);   
  11.     std::cout << *c0.find(3) << std::endl;   
  12.    
  13.     return (0);   
  14.     }   
  15.    
输出结果3


2.unordered_set类模板

定义:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. template<class Key,  
  2.     class Hash = std::hash<Key>,  
  3.     class Pred = std::equal_to<Key>,  
  4.     class Alloc = std::allocator<Key> >  
  5.     class unordered_set;  

Parameters

Parameter

Description

Key

The key type.

Hash

The hash function object type.

Pred

The equality comparison function object type.

Alloc

The allocator class.


结合第一个例子说明一下,
unordered_set是c++11引入的散列容器,在没有冲突的情况下,时间复杂度是常数时间。内部通过hash函数进行弱排序。

在说明unordered_set之前,先说明hash函数。


hash,百度百科中翻译为"散列"或者”哈希“,其通过一个hash函数,建立一个散列表,举个例子:


假设 (员工   工资);   zhao  3550;qian  4600;sun  4300;zhai 5210

设计一个hash函数,姓对应其首字母的ascii码,即zhao(Z)-90,qian(Q)-81,sun(S)-83,zhai(Z)-90

这就是所谓的  h(key)=value,h表示hash函数


这里第一个和第四个冲突,把第四个存在第一个旁边

开辟一个大小为200的数组H,H[90]=3550;H[81]=4600;H[83]=4300;H[91]=5210;

这种做法效率极其低下,但在没有冲突情况下是常数运行时间。


实际中,内存中是这么存储的,separate chaining:



LEN表示这个vec长度,可以看出一个vector,每个vec[i]暂且称为一个node,每个node后面一串叫做buckets,每个buckets是可变大小。



在回过头,讲unordered_set

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. std::unordered_set<int, std::hash<int> > c0;   

 

hash<int>是一个实例化后的函数类

文章开头,

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. size_t operator()(Ty _Val) const;  

实际上是对运算符()重载

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. *c0.find(3)  
相当于

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. *(c0.find(3))  


说明find()之前,需要了解equal_range;

equal_range:返回一个pair,即[X.first, X.second) ,其中X是迭代器,限制了key的范围

[cpp]  view plain  copy
  1. std::pair<iterator, iterator>  
  2.     equal_range(const Key& keyval);  
  3. std::pair<const_iterator, const_iterator>  
  4.     equal_range(const Key& keyval) const;  

[cpp]  view plain  copy
  1. // std_tr1__unordered_set__unordered_set_equal_range.cpp   
  2. // compile with: /EHsc   
  3. #include <unordered_set>   
  4. #include <iostream>   
  5.   
  6. typedef std::unordered_set<char> Myset;   
  7. int main()   
  8.     {   
  9.     Myset c1;   
  10.   
  11.     c1.insert('a');   
  12.     c1.insert('b');   
  13.     c1.insert('c');   
  14.   
  15. // display contents " [c] [b] [a]"   
  16.     for (Myset::const_iterator it = c1.begin();   
  17.         it != c1.end(); ++it)   
  18.         std::cout << " [" << *it << "]";   
  19.     std::cout << std::endl;   
  20.   
  21. // display results of failed search   
  22.     std::pair<Myset::iterator, Myset::iterator> pair1 =   
  23.         c1.equal_range('x');   
  24.     std::cout << "equal_range('x'):";   
  25.     for (; pair1.first != pair1.second; ++pair1.first)   
  26.         std::cout << " [" << *pair1.first << "]";   
  27.     std::cout << std::endl;   
  28.   
  29. // display results of successful search   
  30.     pair1 = c1.equal_range('b');   
  31.     std::cout << "equal_range('b'):";   
  32.     for (; pair1.first != pair1.second; ++pair1.first)   
  33.         std::cout << " [" << *pair1.first << "]";   
  34.     std::cout << std::endl;   
  35.   
  36.     return (0);   
  37.     }   

结果:

[a]  [b]   [c]
equal_range('x'):
equal_range('b'):    [b]


再看一个find的例子

[cpp]  view plain  copy
  1. const_iterator find(const Key& keyval) const;  

find返回 unordered_set::equal_range(keyval).first

[cpp]  view plain  copy
  1. // std_tr1__unordered_set__unordered_set_find.cpp   
  2. // compile with: /EHsc   
  3. #include <unordered_set>   
  4. #include <iostream>   
  5.   
  6. typedef std::unordered_set<char> Myset;   
  7. int main()   
  8. {   
  9.     Myset c1;   
  10.   
  11.     c1.insert('a');   
  12.     c1.insert('b');   
  13.     c1.insert('c');   
  14.   
  15.     // display contents " [c] [b] [a]"   
  16.     // 我用VS2012编译是" [a] [b] [c]"  
  17.     for (Myset::const_iterator it = c1.begin();   
  18.         it != c1.end(); ++it)   
  19.         std::cout << " [" << *it << "]";   
  20.     std::cout << std::endl;   
  21.   
  22.     // try to find and fail   
  23.     std::cout << "find('A') == "   
  24.         << std::boolalpha << (c1.find('A') != c1.end()) << std::endl;   
  25.   
  26.     // try to find and succeed   
  27.     Myset::iterator it = c1.find('b');   
  28.     std::cout << "find('b') == "   
  29.         << std::boolalpha << (it != c1.end())   
  30.         << ": [" << *it << "]" << std::endl;   
  31.   
  32.     return (0);   
  33. }   


结果:

[a]  [b]  [c]

find('A')==false

find('b')==true: [b]

要注意的是:

[cpp]  view plain  copy
  1. c1.end()  
不指向最后一个元素,而指向最后一个元素再+1,当find不到输入的元素时候,返回迭代器就指向这个end()



更复杂的代码分析:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. #include <iostream>   
  2. #include <unordered_set>  
  3.   
  4.   
  5.   
  6.   
  7. using namespace std;  
  8.   
  9.   
  10. size_t hash_function_pair_int(pair<intint> p)  
  11. {  
  12. return hash<int>()(p.first - p.second);  
  13. }  
  14.   
  15.   
  16. typedef  unordered_set < pair<intint>, decltype(hash_function_pair_int)* > set_pair_int;  
  17.   
  18.   
  19. void main()  
  20. {  
  21. set_pair_int boundEdge(10,hash_function_pair_int);  
  22.   
  23.   
  24. pair<int,int> f0(4,3),f1(3,1),f2(3,2),f3(3,2);  
  25. boundEdge.insert(f0);  
  26. boundEdge.insert(f1);  
  27. boundEdge.insert(f2);  
  28. boundEdge.insert(f3);  
  29. }  

关键字decltype自动推断表达式类型;

hash<int>()是一个匿名对象,

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. hash<int>()(p.first - p.second)  

后面第二个()是操作符重载,输入参数是pair的first和second的差

In file included from D:/SelfUsing/Language/CLion 2025.1.3/mingw/lib/gcc/x86_64-w64-mingw32/14.2.0/include/c++/bits/hashtable_policy.h:34, from D:/SelfUsing/Language/CLion 2025.1.3/mingw/lib/gcc/x86_64-w64-mingw32/14.2.0/include/c++/bits/hashtable.h:35, from D:/SelfUsing/Language/CLion 2025.1.3/mingw/lib/gcc/x86_64-w64-mingw32/14.2.0/include/c++/bits/unordered_map.h:33, from D:/SelfUsing/Language/CLion 2025.1.3/mingw/lib/gcc/x86_64-w64-mingw32/14.2.0/include/c++/unordered_map:41, from D:/obj/i wanna own world to me/i wanna own world to me.cpp:9: D:/SelfUsing/Language/CLion 2025.1.3/mingw/lib/gcc/x86_64-w64-mingw32/14.2.0/include/c++/tuple: In instantiation of 'constexpr std::pair<_T1, _T2>::pair(std::tuple<_Args1 ...>&, std::tuple<_Args2 ...>&, std::_Index_tuple<_Indexes1 ...>, std::_Index_tuple<_Indexes2 ...>) [with _Args1 = {std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&}; long long unsigned int ..._Indexes1 = {0}; _Args2 = {}; long long unsigned int ..._Indexes2 = {}; _T1 = const std::__cxx11::basic_string<char>; _T2 = SDL_GL_Image]': D:/SelfUsing/Language/CLion 2025.1.3/mingw/lib/gcc/x86_64-w64-mingw32/14.2.0/include/c++/tuple:2877:63: required from 'constexpr std::pair<_T1, _T2>::pair(std::piecewise_construct_t, std::tuple<_Args1 ...>, std::tuple<_Args2 ...>) [with _Args1 = {std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&}; _Args2 = {}; _T1 = const std::__cxx11::basic_string<char>; _T2 = SDL_GL_Image]' 2877 | typename _Build_index_tuple<sizeof...(_Args2)>::__type()) | ^ D:/SelfUsing/Language/CLion 2025.1.3/mingw/lib/gcc/x86_64-w64-mingw32/14.2.0/include/c++/bits/stl_construct.h:97:14: required from 'constexpr decltype (::new(void*(0)) _Tp) std::construct_at(_Tp*, _Args&& ...) [with _Tp = pair<const __cxx11::basic_string<char>, SDL_GL_Image>; _Args = {const piecewise_construct_t&, tuple<__cxx11::basic_string<char, char_traits<char>, allocator<char> >&&>, tuple<>}; decltype (::new(void*(0)) _Tp) = pair<const __cxx11::basic_string<char>, SDL_GL_Image>*]' 97 | { return ::new((void*)__location) _Tp(std::forward<_Args>(__args)...); } | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ D:/SelfUsing/Language/CLion 2025.1.3/mingw/lib/gcc/x86_64-w64-mingw32/14.2.0/include/c++/bits/alloc_traits.h:536:21: required from 'static constexpr void std::allocator_traits<std::allocator<_Up> >::construct(allocator_type&, _Up*, _Args&& ...) [with _Up = std::pair<const std::__cxx11::basic_string<char>, SDL_GL_Image>; _Args = {const std::piecewise_construct_t&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&>, std::tuple<>}; _Tp = std::__detail::_Hash_node<std::pair<const std::__cxx11::basic_string<char>, SDL_GL_Image>, true>; allocator_type = std::allocator<std::__detail::_Hash_node<std::pair<const std::__cxx11::basic_string<char>, SDL_GL_Image>, true> >]' 536 | std::construct_at(__p, std::forward<_Args>(__args)...); | ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ D:/SelfUsing/Language/CLion 2025.1.3/mingw/lib/gcc/x86_64-w64-mingw32/14.2.0/include/c++/bits/hashtable_policy.h:2024:36: required from 'std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type* std::__detail::_Hashtable_alloc<_NodeAlloc>::_M_allocate_node(_Args&& ...) [with _Args = {const std::piecewise_construct_t&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&>, std::tuple<>}; _NodeAlloc = std::allocator<std::__detail::_Hash_node<std::pair<const std::__cxx11::basic_string<char>, SDL_GL_Image>, true> >; __node_ptr = std::allocator<std::__detail::_Hash_node<std::pair<const std::__cxx11::basic_string<char>, SDL_GL_Image>, true> >::value_type*]' 2024 | __node_alloc_traits::construct(__alloc, __n->_M_valptr(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~ 2025 | std::forward<_Args>(__args)...); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ D:/SelfUsing/Language/CLion 2025.1.3/mingw/lib/gcc/x86_64-w64-mingw32/14.2.0/include/c++/bits/hashtable.h:312:35: required from 'std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::_Scoped_node::_Scoped_node(std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::__hashtable_alloc*, _Args&& ...) [with _Args = {const std::piecewise_construct_t&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&>, std::tuple<>}; _Key = std::__cxx11::basic_string<char>; _Value = std::pair<const std::__cxx11::basic_string<char>, SDL_GL_Image>; _Alloc = std::allocator<std::pair<const std::__cxx11::basic_string<char>, SDL_GL_Image> >; _ExtractKey = std::__detail::_Select1st; _Equal = std::equal_to<std::__cxx11::basic_string<char> >; _Hash = std::hash<std::__cxx11::basic_string<char> >; _RangeHash = std::__detail::_Mod_range_hashing; _Unused = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, false, true>; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::__hashtable_alloc = std::_Hashtable<std::__cxx11::basic_string<char>, std::pair<const std::__cxx11::basic_string<char>, SDL_GL_Image>, std::allocator<std::pair<const std::__cxx11::basic_string<char>, SDL_GL_Image> >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char> >, std::hash<std::__cxx11::basic_string<char> >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::__hashtable_alloc]' 312 | _M_node(__h->_M_allocate_node(std::forward<_Args>(__args)...)) | ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ D:/SelfUsing/Language/CLion 2025.1.3/mingw/lib/gcc/x86_64-w64-mingw32/14.2.0/include/c++/bits/hashtable_policy.h:870:42: required from 'std::__detail::_Map_base<_Key, std::pair<const _Key, _Val>, _Alloc, std::__detail::_Select1st, _Equal, _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits, true>::mapped_type& std::__detail::_Map_base<_Key, std::pair<const _Key, _Val>, _Alloc, std::__detail::_Select1st, _Equal, _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits, true>::operator[](key_type&&) [with _Key = std::__cxx11::basic_string<char>; _Val = SDL_GL_Image; _Alloc = std::allocator<std::pair<const std::__cxx11::basic_string<char>, SDL_GL_Image> >; _Equal = std::equal_to<std::__cxx11::basic_string<char> >; _Hash = std::hash<std::__cxx11::basic_string<char> >; _RangeHash = std::__detail::_Mod_range_hashing; _Unused = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, false, true>; mapped_type = SDL_GL_Image; key_type = std::__cxx11::basic_string<char>]' 870 | typename __hashtable::_Scoped_node __node { | ^~~~~~ D:/SelfUsing/Language/CLion 2025.1.3/mingw/lib/gcc/x86_64-w64-mingw32/14.2.0/include/c++/bits/unordered_map.h:992:20: required from 'std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type& std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::operator[](key_type&&) [with _Key = std::__cxx11::basic_string<char>; _Tp = SDL_GL_Image; _Hash = std::hash<std::__cxx11::basic_string<char> >; _Pred = std::equal_to<std::__cxx11::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::__cxx11::basic_string<char>, SDL_GL_Image> >; mapped_type = SDL_GL_Image; key_type = std::__cxx11::basic_string<char>]' 992 | { return _M_h[std::move(__k)]; } | ~~~~^ D:/obj/i wanna own world to me/i wanna own world to me.cpp:164:57: required from here 164 | glBindTexture(GL_TEXTURE_2D, images["sprBrownBlock"s].textureID); | ^ D:/SelfUsing/Language/CLion 2025.1.3/mingw/lib/gcc/x86_64-w64-mingw32/14.2.0/include/c++/tuple:2888:9: error: no matching function for call to 'SDL_GL_Image::SDL_GL_Image()' 2888 | second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ D:/obj/i wanna own world to me/i wanna own world to me.cpp:43:14: note: candidate: 'SDL_GL_Image::SDL_GL_Image(const char*)' 43 | explicit SDL_GL_Image(const char* path) { | ^~~~~~~~~~~~ D:/obj/i wanna own world to me/i wanna own world to me.cpp:43:14: note: candidate expects 1 argument, 0 provided D:/obj/i wanna own world to me/i wanna own world to me.cpp:39:7: note: candidate: 'constexpr SDL_GL_Image::SDL_GL_Image(const SDL_GL_Image&)' 39 | union SDL_GL_Image { | ^~~~~~~~~~~~ D:/obj/i wanna own world to me/i wanna own world to me.cpp:39:7: note: candidate expects 1 argument, 0 provided ninja: build stopped: subcommand failed.
08-05
<think>我们正在讨论的是在C++中使用`unordered_map`插入`SDL_GL_Image`对象时遇到的错误,错误信息是“default constructor missing”。这意味着在尝试插入元素时,`unordered_map`需要调用`SDL_GL_Image`的默认构造函数,但该构造函数不存在。 ### 问题分析 `std::unordered_map`的`insert`方法在插入元素时,有几种方式,其中一种是通过`std::pair`来插入键值对。但有时,`unordered_map`可能会尝试使用值类型的默认构造函数来创建临时对象,然后再进行复制或移动。如果值类型(这里是`SDL_GL_Image`)没有默认构造函数,就会导致编译错误。 具体来说,当我们使用`unordered_map::insert`的其中一种形式,例如: ```cpp std::unordered_map<Key, T> myMap; myMap.insert(std::make_pair(key, T())); // 这里需要T有默认构造函数 ``` 或者在某些情况下,`unordered_map`内部可能会尝试构造一个默认初始化的值对象。 ### 解决方案 1. **避免需要默认构造函数**:使用`unordered_map`的`emplace`方法或`insert`方法中不需要默认构造函数的版本。 - `emplace`方法:直接在容器内构造元素,避免临时对象的创建。这样,我们可以使用`SDL_GL_Image`的任意构造函数。 ```cpp myMap.emplace(key, arg1, arg2, ...); // 传递构造SDL_GL_Image所需的参数 ``` 或者,如果`SDL_GL_Image`支持移动构造,也可以这样: ```cpp myMap.emplace(key, SDL_GL_Image(...)); // 使用参数构造一个临时对象,然后移动 ``` 2. **使用`insert`的正确形式**:如果一定要用`insert`,可以传递一个已经构造好的键值对,这样就不会调用默认构造函数。 ```cpp myMap.insert({key, SDL_GL_Image(arg1, arg2, ...)}); ``` 3. **检查`SDL_GL_Image`的构造函数**:确保你有一个合适的构造函数来创建对象。如果`SDL_GL_Image`没有默认构造函数,那么你就不能无参构造它,必须提供参数。 ### 示例代码 假设`SDL_GL_Image`有一个构造函数,它接受一个文件路径字符串作为参数: ```cpp class SDL_GL_Image { public: SDL_GL_Image(const std::string& filepath); // 构造函数,需要文件路径 // ... 没有默认构造函数 }; ``` 那么,在`unordered_map`中插入元素可以这样: ```cpp #include <unordered_map> #include <string> int main() { std::unordered_map<std::string, SDL_GL_Image> imageMap; // 使用emplace imageMap.emplace("texture1", "path/to/image1.png"); // 或者使用insert imageMap.insert(std::make_pair("texture2", SDL_GL_Image("path/to/image2.png"))); return 0; } ``` ### 注意事项 - 如果`SDL_GL_Image`的拷贝构造函数被删除(例如,因为资源管理需要移动语义),那么你可能需要使用`emplace`来直接构造,或者使用移动语义(如`std::move`)。 - 如果`SDL_GL_Image`对象很大,考虑使用指针(如`std::unique_ptr`)存储在`unordered_map`中,以避免拷贝开销,同时也能解决默认构造问题(因为指针有默认构造)。但这样会增加一层间接访问。 ### 总结 错误“default constructor missing”是因为`unordered_map`的某些操作需要值类型具有默认构造函数。解决方法是通过`emplace`或带参数的`insert`来插入元素,避免调用默认构造函数。同时,确保提供正确的参数来调用`SDL_GL_Image`的构造函数。 如果问题仍然存在,请检查`SDL_GL_Image`的构造函数是否可用,以及是否所有必要的参数都已提供。 §§相关问题§§ 1. C++中`emplace`和`insert`有什么区别? 2. 如何为自定义类实现移动语义? 3. 在`unordered_map`中存储大型对象的最佳实践是什么? 4. 如何避免`unordered_map`插入时的拷贝开销?</think>### 解决C++ `unordered_map`插入`SDL_GL_Image`时缺少默认构造函数的错误 当您在C++中使用`std::unordered_map`插入`SDL_GL_Image`对象时遇到"default constructor missing"错误,这是因为`unordered_map`的某些操作需要值类型具有默认构造函数。以下是详细分析和解决方案: #### 问题原因分析 1. **`unordered_map`的底层机制**: - `unordered_map`在插入元素时(如`insert()`或`operator[]`),可能需要先构造一个值类型的默认初始化对象 - 当您尝试访问不存在的键时(如`map[key]`),容器会自动插入一个默认构造的值对象 - 如果值类型`SDL_GL_Image`没有默认构造函数,就会触发编译错误 2. **SDL_GL_Image的限制**: - SDL图像类通常禁用默认构造函数,因为它们需要特定资源初始化 - 错误示例代码: ```cpp std::unordered_map<std::string, SDL_GL_Image> textureMap; textureMap["player"]; // 需要默认构造SDL_GL_Image → 错误! ``` #### 解决方案 ##### 方法1:使用`emplace()`直接构造(推荐) ```cpp std::unordered_map<std::string, SDL_GL_Image> textureMap; // 使用emplace直接传递构造参数 textureMap.emplace("player", "textures/player.png", SDL_IMAGE_FORMAT_PNG); // 或者使用try_emplace(C++17+) textureMap.try_emplace("enemy", "textures/enemy.jpg", SDL_IMAGE_FORMAT_JPEG); ``` - ✅ 优点:避免临时对象构造,直接使用参数创建对象 - ✅ 无需默认构造函数 ##### 方法2:使用指针存储 ```cpp std::unordered_map<std::string, std::unique_ptr<SDL_GL_Image>> textureMap; // 插入时直接构造对象 textureMap["player"] = std::make_unique<SDL_GL_Image>("textures/player.png"); // 访问时使用指针语法 if (textureMap.find("player") != textureMap.end()) { textureMap["player"]->Render(); // 使用->操作符 } ``` - ✅ 优点:避免对象拷贝,支持多态 - ✅ 指针类型有默认构造函数(nullptr) ##### 方法3:自定义默认构造行为 如果必须使用值语义,可通过包装类实现: ```cpp struct TextureWrapper { std::optional<SDL_GL_Image> texture; TextureWrapper() = default; // 默认构造合法 // 延迟初始化 void Init(const std::string& path) { texture.emplace(path, SDL_IMAGE_FORMAT_AUTO); } }; std::unordered_map<std::string, TextureWrapper> textureMap; textureMap["player"].Init("textures/player.png"); ``` #### 关键注意事项 1. **避免使用`operator[]`进行查找**: ```cpp // 错误:可能触发默认构造 auto& tex = textureMap["missing_texture"]; // 正确:使用find() if (auto it = textureMap.find("player"); it != textureMap.end()) { it->second.Render(); } ``` 2. **插入前检查键是否存在**: ```cpp if (textureMap.count("new_texture") == 0) { textureMap.emplace("new_texture", "path/to/texture.png"); } ``` 3. **C++版本影响**: - C++17+:优先使用`try_emplace()`,性能更好且不要求可默认构造 - C++11:使用`emplace()`或指针方案 #### 完整示例 ```cpp #include <SDL_image.h> #include <unordered_map> #include <memory> int main() { std::unordered_map<std::string, std::unique_ptr<SDL_GL_Image>> textures; // 安全插入 textures.emplace("background", std::make_unique<SDL_GL_Image>("bg.png", SDL_IMAGE_FORMAT_PNG)); // 安全访问 if (auto it = textures.find("background"); it != textures.end()) { it->second->Render(); } // 添加新纹理(避免operator[]) textures.emplace("character", std::make_unique<SDL_GL_Image>("char.png", SDL_IMAGE_FORMAT_PNG)); return 0; } ``` 通过上述方法,您可以绕过SDL_GL_Image缺少默认构造函数的问题,同时保持代码的安全性和高效性。选择方案时优先考虑`emplace`/`try_emplace`或智能指针方案,它们能最好地满足资源管理需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值