-1

Let the code speak it:

auto SetCompare = [](const string& a, const string& b)
{
    size_t L = a.length();
    size_t R = b.length();
    if (L == R) return (a < b);
    return L < R;
};

using MySet = std::set<string, decltype(SetCompare)>;

unordered_map<string, MySet> Map;

The insertion or access won't work:

Map["abc"];
Map["xyz"].insert("mapped to xyz");

// Insert TO set works
MySet mySet(SetCompare); // HINT
mySet.insert("x");
mySet.insert("abc");

// But not to the map!
Map.insert({"pqr", mySet});

At the HINTed location, I pass SetCompare lambda (not just type) to MySet's constructor. The question is how to pass it to the "Value" type of unordered_map?

EDIT (Compiler errors):

GCC C++17

In file included from /usr/include/c++/7/set:60:0,
                 from main.cpp:10:
/usr/include/c++/7/bits/stl_tree.h: In instantiation of ‘std::_Rb_tree_key_compare<_Key_compare>::_Rb_tree_key_compare() [with _Key_compare = main()::<lambda(const string&, const string&)>]’:
/usr/include/c++/7/bits/stl_tree.h:688:4:   required from ‘std::pair<_T1, _T2>::pair(std::tuple<_Args1 ...>&, std::tuple<_Args2 ...>&, std::_Index_tuple<_Indexes1 ...>, std::_Index_tuple<_Indexes2 ...>) [with _Args1 = {char&&}; long unsigned int ..._Indexes1 = {0}; _Args2 = {}; long unsigned int ..._Indexes2 = {}; _T1 = const char; _T2 = std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> >]’
/usr/include/c++/7/tuple:1641:63:   required from ‘std::pair<_T1, _T2>::pair(std::piecewise_construct_t, std::tuple<_Args1 ...>, std::tuple<_Args2 ...>) [with _Args1 = {char&&}; _Args2 = {}; _T1 = const char; _T2 = std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> >]’
/usr/include/c++/7/ext/new_allocator.h:136:4:   required from ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >; _Args = {const std::piecewise_construct_t&, std::tuple<char&&>, std::tuple<>}; _Tp = std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >]’
/usr/include/c++/7/bits/alloc_traits.h:475:4:   required from ‘static void std::allocator_traits<std::allocator<_CharT> >::construct(std::allocator_traits<std::allocator<_CharT> >::allocator_type&, _Up*, _Args&& ...) [with _Up = std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >; _Args = {const std::piecewise_construct_t&, std::tuple<char&&>, std::tuple<>}; _Tp = std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >; std::allocator_traits<std::allocator<_CharT> >::allocator_type = std::allocator<std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > > >]’
/usr/include/c++/7/bits/hashtable_policy.h:2066:37:   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<char&&>, std::tuple<>}; _NodeAlloc = std::allocator<std::__detail::_Hash_node<std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >, false> >; std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type = std::__detail::_Hash_node<std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >, false>]’
/usr/include/c++/7/bits/hashtable_policy.h:750:8:   required from ‘std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::mapped_type& std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::operator[](std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::key_type&&) [with _Key = char; _Pair = std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >; _Alloc = std::allocator<std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > > >; _Equal = std::equal_to<char>; _H1 = std::hash<char>; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<false, false, true>; std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::mapped_type = std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> >; std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::key_type = char]’
/usr/include/c++/7/bits/unordered_map.h:977:20:   required from ‘std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type& std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::operator[](std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type&&) [with _Key = char; _Tp = std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> >; _Hash = std::hash<char>; _Pred = std::equal_to<char>; _Alloc = std::allocator<std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > > >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type = std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type = char]’
<span class="error_line" onclick="ide.gotoLine('main.cpp',31)">main.cpp:31:7</span>:   required from here
/usr/include/c++/7/bits/stl_tree.h:149:24: error: use of deleted function ‘main()::::()’
       : _M_key_compare()
                        ^
main.cpp:17:24: note: a lambda closure type has a deleted default constructor
     auto SetCompare = [](const string& a, const string& b)

GCC C++17

In file included from /usr/include/c++/6/set:60:0,
                 from main.cpp:10:
/usr/include/c++/6/bits/stl_tree.h: In instantiation of ‘std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Rb_tree_impl<_Key_compare, <anonymous> >::_Rb_tree_impl() [with _Key_compare = main()::<lambda(const string&, const string&)>; bool <anonymous> = false; _Key = std::basic_string<char>; _Val = std::basic_string<char>; _KeyOfValue = std::_Identity<std::basic_string<char> >; _Compare = main()::<lambda(const string&, const string&)>; _Alloc = std::allocator<std::basic_string<char> >]’:
/usr/include/c++/6/bits/stl_tree.h:821:18:   required from ‘std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Rb_tree() [with _Key = std::basic_string<char>; _Val = std::basic_string<char>; _KeyOfValue = std::_Identity<std::basic_string<char> >; _Compare = main()::<lambda(const string&, const string&)>; _Alloc = std::allocator<std::basic_string<char> >]’
/usr/include/c++/6/bits/stl_set.h:146:14:   required from ‘std::set<_Key, _Compare, _Alloc>::set() [with _Key = std::basic_string<char>; _Compare = main()::<lambda(const string&, const string&)>; _Alloc = std::allocator<std::basic_string<char> >]’
/usr/include/c++/6/tuple:1590:70:   required from ‘std::pair<_T1, _T2>::pair(std::tuple<_Args1 ...>&, std::tuple<_Args2 ...>&, std::_Index_tuple<_Indexes1 ...>, std::_Index_tuple<_Indexes2 ...>) [with _Args1 = {char&&}; long unsigned int ..._Indexes1 = {0ul}; _Args2 = {}; long unsigned int ..._Indexes2 = {}; _T1 = const char; _T2 = std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> >]’
/usr/include/c++/6/tuple:1579:63:   required from ‘std::pair<_T1, _T2>::pair(std::piecewise_construct_t, std::tuple<_Args1 ...>, std::tuple<_Args2 ...>) [with _Args1 = {char&&}; _Args2 = {}; _T1 = const char; _T2 = std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> >]’
/usr/include/c++/6/ext/new_allocator.h:120:4:   required from ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >; _Args = {const std::piecewise_construct_t&, std::tuple<char&&>, std::tuple<>}; _Tp = std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >]’
/usr/include/c++/6/bits/alloc_traits.h:475:4:   required from ‘static void std::allocator_traits<std::allocator<_CharT> >::construct(std::allocator_traits<std::allocator<_CharT> >::allocator_type&, _Up*, _Args&& ...) [with _Up = std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >; _Args = {const std::piecewise_construct_t&, std::tuple<char&&>, std::tuple<>}; _Tp = std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >; std::allocator_traits<std::allocator<_CharT> >::allocator_type = std::allocator<std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > > >]’
/usr/include/c++/6/bits/hashtable_policy.h:1953:37:   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<char&&>, std::tuple<>}; _NodeAlloc = std::allocator<std::__detail::_Hash_node<std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >, false> >; std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type = std::__detail::_Hash_node<std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >, false>]’
/usr/include/c++/6/bits/hashtable_policy.h:620:8:   required from ‘std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::mapped_type& std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::operator[](std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::key_type&&) [with _Key = char; _Pair = std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >; _Alloc = std::allocator<std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > > >; _Equal = std::equal_to<char>; _H1 = std::hash<char>; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<false, false, true>; std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::mapped_type = std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> >; std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::key_type = char]’
/usr/include/c++/6/bits/unordered_map.h:908:20:   required from ‘std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type& std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::operator[](std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type&&) [with _Key = char; _Tp = std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> >; _Hash = std::hash<char>; _Pred = std::equal_to<char>; _Alloc = std::allocator<std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > > >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type = std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type = char]’
<span class="error_line" onclick="ide.gotoLine('main.cpp',31)">main.cpp:31:7</span>:   required from here
/usr/include/c++/6/bits/stl_tree.h:602:21: error: use of deleted function ‘main()::::()’
      _M_node_count(0)
                     ^
main.cpp:17:24: note: a lambda closure type has a deleted default constructor
     auto SetCompare = [](const string& a, const string& b)
                        ^
In file included from /usr/include/c++/6/set:60:0,
                 from main.cpp:10:
/usr/include/c++/6/bits/stl_tree.h:628:4: warning: ‘void std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Rb_tree_impl<_Key_compare,  >::_M_initialize() [with _Key_compare = main()::; bool  = false; _Key = std::basic_string; _Val = std::basic_string; _KeyOfValue = std::_Identity >; _Compare = main()::; _Alloc = std::allocator >]’ used but never defined
    _M_initialize()
    ^~~~~~~~~~~~~

VC C++14/17

1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\set(84,1): error C2280: 'main::<lambda_b1395bcc88464fd844969fa0764f137c>::<lambda_b1395bcc88464fd844969fa0764f137c>(void)': attempting to reference a deleted function
1>xx.cpp(923): message : see declaration of 'main::<lambda_b1395bcc88464fd844969fa0764f137c>::<lambda_b1395bcc88464fd844969fa0764f137c>'
1>xxx.cpp(923,20): message : 'main::<lambda_b1395bcc88464fd844969fa0764f137c>::<lambda_b1395bcc88464fd844969fa0764f137c>(void)': function was explicitly deleted
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\set(84): message : while compiling class template member function 'std::set<std::string,main::<lambda_b1395bcc88464fd844969fa0764f137c>,std::allocator<std::string>>::set(void)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\tuple(975): message : see reference to function template instantiation 'std::set<std::string,main::<lambda_b1395bcc88464fd844969fa0764f137c>,std::allocator<std::string>>::set(void)' being compiled
1>xxx.cpp(937): message : see reference to class template instantiation 'std::set<std::string,main::<lambda_b1395bcc88464fd844969fa0764f137c>,std::allocator<std::string>>' being compiled
Ajay
  • 18,086
  • 12
  • 59
  • 105
  • `Map.emplace("abc", MySet(SetCompare))` . It's possible to pass `SetCompare` everywhere, but the experience is going to be miserable. Write a proper named class with a suitable `operator()`, that can be default-constructed, and use that as a comparator. – Igor Tandetnik Nov 29 '20 at 15:16
  • 3
    What does "won't work" mean? Did you get a compilation error? What was it? Did you try to understand what the compilation error meant? It is true that nobody managed to invent a C++ compiler that has a reputation for clean and concise error messages, but anyone whose goal is to become skilled in C++ should make at least an attempt to read and interpret their compiler's error messages. Eventually they'll succeed and figure out one of them. Then another. Then more. And then they'll always be able to figure out their compilers' errors without having to ask anyone else to help. – Sam Varshavchik Nov 29 '20 at 15:20
  • @SamVarshavchik, Different compiler would give different error messages, depending on context (using `[]` operator, `insert`, at constructor), and based on C++14 or C++17, and hence compiler error message isn't important. I see only VC++2019 with `/std:c++latest` can compile it (Didn't try with gcc, clang with such option - if any). – Ajay Nov 29 '20 at 15:23
  • 1
    The fact that different compilers give different messages is *even more* useful. Read all of them, I can assure you they all have some useful information to give. Especially if some compilers accept the code, and others don't, that's very useful information to know. – cigien Nov 29 '20 at 15:24
  • @bolov, No it doesn't! "x" will appear before "abc". – Ajay Nov 29 '20 at 15:28
  • 1
    Starting that "compiler error message isn't important" is synonymous with "I will always need others to help me with my compiliation errors". At some point you will have to concede that this is not going to be practical in the long run. It is true that different compilers give different error messages. But they all report the same problems. After one becomes good at deciphering their own compiler's errors, deciphering other compilers' cryptic errors is several orders of magnitude easier. I don't use VS, but I can generally figure out what it's barking about. – Sam Varshavchik Nov 29 '20 at 15:33
  • 1
    @SamVarshavchik Added compiler errors. – Ajay Nov 29 '20 at 15:37
  • 1
    And it's complaining about the lambda class's default constructor being deleted. `std::map::operator[]` default-constructs the map value if it doesn't exist, hence the value of the map must be default-constructible. Lambda classes have default constructors only with C++20, or later. – Sam Varshavchik Nov 29 '20 at 15:40
  • You expected compiler error to see clearly what the compiler is reporting, and now you are carelessly talking about `std::map`? I didn't use `std::map`. I need you to please add something more useful/valuable after I posted the errors. – Ajay Nov 29 '20 at 15:42
  • 1
    You used `std::unordered_map`, to which the same thing applies. Distinction without a difference. Please pay attention. – Sam Varshavchik Nov 29 '20 at 15:43
  • In this respect, their requirements are the same. Nothing further needs to be said, if you still don't understand the reason for the compilation failure I can't explain it any further than that. Still deciding whether to close this question as a dupe of https://stackoverflow.com/questions/32911729/understanding-how-lambda-closure-type-has-deleted-default-constructor – Sam Varshavchik Nov 29 '20 at 15:46
  • @SamVarshavchik: It's *not* a duplicate of that topic. – Nawaz Nov 29 '20 at 16:30

1 Answers1

3

Lambdas are never default constructible before C++20 (see ClosureType::ClosureType() section on cppreference), so you must pass the SetCompare variable when constructing a MySet. Using -std=c++20/-std=c++2a on clang or GCC should work (demo), as well as /std:c++latest on MSVC.

To work around this limitation in earlier C++ versions define SetCompare as a functor type:

struct SetCompare
{
    bool operator()(const string& a, const string& b)
    {
        size_t L = a.length();
        size_t R = b.length();
        if (L == R) return (a < b);
        return L < R;
    }
};

and use this type when defining MySet

using MySet = std::set<std::string, SetCompare>;
IlCapitano
  • 1,994
  • 1
  • 7
  • 15