1

Are there any real benefits for me to migrate my useage of CMapStringToString over to std::map<CString, CString> in my application?


I don't understand, I tried to change the code and now it won't compile:

7>------ Build started: Project: Meeting Schedule Assistant, Configuration: Release x64 ------
7>CalendarSettingsOutlookPage.cpp
7>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\include\xhash(112,53): error C2064: term does not evaluate to a function taking 1 arguments
7>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\include\xhash(135): message : see reference to variable template 'const bool _Nothrow_hash<std::hash<ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t> > > >,ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t> > > >' being compiled
7>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\include\xhash(135): message : while compiling class template member function 'size_t std::_Uhash_compare<_Kty,_Hasher,_Keyeq>::operator ()<_Kty>(const _Keyty &) noexcept(<expr>) const'
7>        with
7>        [
7>            _Kty=CString,
7>            _Hasher=std::hash<CString>,
7>            _Keyeq=std::equal_to<CString>,
7>            _Keyty=ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>
7>        ]
7>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\include\xhash(1109): message : see reference to variable template 'const bool _Nothrow_hash<std::_Umap_traits<ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t> > >,ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t> > >,std::_Uhash_compare<ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t> > >,std::hash<ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t> > > >,std::equal_to<ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t> > > > >,std::allocator<std::pair<ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t> > > const ,ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t> > > > >,0>,ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t> > > >' being compiled
7>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\include\xhash(1096): message : while compiling class template member function 'void std::_Hash<std::_Umap_traits<_Kty,_Ty,std::_Uhash_compare<_Kty,_Hasher,_Keyeq>,_Alloc,false>>::clear(void) noexcept'
7>        with
7>        [
7>            _Kty=CString,
7>            _Ty=CString,
7>            _Hasher=std::hash<CString>,
7>            _Keyeq=std::equal_to<CString>,
7>            _Alloc=std::allocator<std::pair<const CString,CString>>
7>        ]
7>D:\My Programs\2022\MeetSchedAssist\Meeting Schedule Assistant\CalendarSettingsOutlookPage.cpp(156): message : see reference to function template instantiation 'void std::_Hash<std::_Umap_traits<_Kty,_Ty,std::_Uhash_compare<_Kty,_Hasher,_Keyeq>,_Alloc,false>>::clear(void) noexcept' being compiled
7>        with
7>        [
7>            _Kty=CString,
7>            _Ty=CString,
7>            _Hasher=std::hash<CString>,
7>            _Keyeq=std::equal_to<CString>,
7>            _Alloc=std::allocator<std::pair<const CString,CString>>
7>        ]
7>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\include\unordered_map(67): message : see reference to class template instantiation 'std::_Hash<std::_Umap_traits<_Kty,_Ty,std::_Uhash_compare<_Kty,_Hasher,_Keyeq>,_Alloc,false>>' being compiled
7>        with
7>        [
7>            _Kty=CString,
7>            _Ty=CString,
7>            _Hasher=std::hash<CString>,
7>            _Keyeq=std::equal_to<CString>,
7>            _Alloc=std::allocator<std::pair<const CString,CString>>
7>        ]
7>D:\My Programs\2022\MeetSchedAssist\Meeting Schedule Assistant\CalendarSettingsOutlookPage.h(59): message : see reference to class template instantiation 'std::unordered_map<CString,CString,std::hash<CString>,std::equal_to<CString>,std::allocator<std::pair<const CString,CString>>>' being compiled
7>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\include\xhash(111,44): error C2056: illegal expression
7>Done building project "Meeting Schedule Assistant.vcxproj" -- FAILED.

If I use std::map<CString, CString> it works but std::unordered_map<CString, CString> throws out the errors above. My usage calls:

  • if (m_mapCalendarList.find(strCalendarName) != m_mapCalendarList.end())
  • if (strCalendarId == m_mapCalendarList[strCalendarName])
  • m_mapCalendarList.clear();

The variable is defined in the header.

Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164
  • 3
    One benefit is that you could now write `for (auto const& entry : my_map) { ... }`. Whether that is a real benefit to your application is not something we can answer. Also, I don't know whether MFC's dictionary types are sorted. If they aren't, a closer equivalent would be [`unordered_map`](https://en.cppreference.com/w/cpp/container/unordered_map). – IInspectable Nov 10 '21 at 08:40
  • @IInspectable They are not sorted. And valid point about `for` loops. In this particular case I am building the map once and then using it to lookup values to see if they exist. Eg: `(m_mapCalendarList.Lookup(strCalendarName, strCalendarId)`. I don't actual iterate the collection. – Andrew Truckle Nov 10 '21 at 08:48
  • @AlexF Thanks for observations. For this application I use `CString` heavily so felt it best to retain it for consistency at this time. – Andrew Truckle Nov 10 '21 at 08:55
  • @IInspectable If I use `std::map` it compiles. If I use `std::unordered_map` it does not compile. – Andrew Truckle Nov 10 '21 at 09:15
  • 4
    `std::map` only requires that `std::less` is implemented (which, for `CString` it is). `std::unordered_map`, on the other hand, needs a hashing function. You would need to provide that yourself (see [`std::hash`](https://en.cppreference.com/w/cpp/utility/hash) for details). Since you are populating the dictionary only once, using a `std::map` doesn't hurt, though. – IInspectable Nov 10 '21 at 09:30
  • If you want to migrate your app to anything non MFC, yes! We are in a era where non-Windows platforms development is being asked, and MFC is not supported there! I am surprised of your comparison being between ` CMapStringToString` vs `std::map` instead of sthg like `CMap` vs `std::map`! – sergiol Nov 10 '21 at 15:37
  • 1
    BTW, even Microsoft recommends to stay away from their own archaic MFC collections, and to use the `std` ones instead. https://stackoverflow.com/a/1348138/383779 – sergiol Nov 10 '21 at 15:46
  • @sergiol I never used the CMap container in my app. That’s why. – Andrew Truckle Nov 10 '21 at 16:15
  • @sergiol `CMapStringToString` is older than the `CMap`. I'm also working on code that still contains some `CMapStringToString` – Jabberwocky Nov 12 '21 at 08:51
  • @Jabberwocky: I know, but I am surprised people nowadays is still using it. We are two steps ahead of hard-coded `CMapSOMETHINGToSOMEWHAT` very ungeneric classes, 1. is `CMap`, 2. is `std::map`. – sergiol Nov 12 '21 at 14:21

1 Answers1

4

MFC hash maps have some drawbacks, except from just being not C++ Standard and not portable:

  • Do not recalculate hash table automatically. Not even able to resize hash table when populated. Default size of hash table may be suboptimal for you.
  • As noted in comments, they don't support range-based for, or ranges algorithm
  • Not copyable and not movable by themselves. Moving a container may be useful
  • Don't support move semantic for element insertion, this involves some overhead

For std::unordered_map, you need to implement hash function, and pass it as the corresponding template parameter. Or specialize standard hash for CString. But I prefer the former option.

And would be good to pass also comparison function, that performs locale-independent comparison. Since with a hash that is not aware of locale, you can't have locale-aware map anyway, but CString::operator== is locale-aware.

As there's HashKey that can take LPCSTR and LPCWSTR that is used by CMap, implementation of own STL-compatible hashes is trivial.


You can completely avoid dealing with hashes, and use std::unordered_map<std::string, std::string>. But transition to std::string will cause way more changes I guess.

Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164
Alex Guteniev
  • 12,039
  • 2
  • 34
  • 79