Search code examples
c++lambdacomparator

Using a container inside a custom comparator for comparison


#include <set>
#include <string>
#include <unordered_map>
#include <vector>

int main()
{
    std::vector<std::string> wordsContainer={"aba", "baba","ris", "u"};
    auto cmp=[&](int i, int j){
        if (wordsContainer[i].size() != wordsContainer[j].size())
            return wordsContainer[i].size() < wordsContainer[j].size();
        return i < j;
    };
    std::unordered_map<int,std::set<int,decltype(cmp)>>mp;
    mp[0].insert(2);  // <-- this line is the issue
}

I'm trying to do something like this, in C++20. I wanted my container be sorted based on some other container. But compiler complains about the type of cmp, removing & from [&] works but then I don't have access to variable wordsContainer.

In file included from /opt/rh/devtoolset-10/root/usr/include/c++/10/map:60,
                 from /opt/rh/devtoolset-10/root/usr/include/c++/10/x86_64-redhat-linux/bits/stdc++.h:81,
                 from main.cpp:1:
/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_tree.h: In instantiation of ‘std::_Rb_tree_key_compare<_Key_compare>::_Rb_tree_key_compare() [with _Key_compare = main()::<lambda(int, int)>]’:
/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_tree.h:688:22:   required from ‘std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Rb_tree_impl<_Key_compare, <anonymous> >::_Rb_tree_impl() [with _Key_compare = main()::<lambda(int, int)>; bool <anonymous> = false; _Key = int; _Val = int; _KeyOfValue = std::_Identity<int>; _Compare = main()::<lambda(int, int)>; _Alloc = std::allocator<int>]’
/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_tree.h:935:7:   required from ‘constexpr std::pair<_T1, _T2>::pair(std::tuple<_Args1 ...>&, std::tuple<_Args2 ...>&, std::_Index_tuple<_Indexes1 ...>, std::_Index_tuple<_Indexes2 ...>) [with _Args1 = {int&&}; long unsigned int ..._Indexes1 = {0}; _Args2 = {}; long unsigned int ..._Indexes2 = {}; _T1 = const int; _T2 = std::__debug::set<int, main()::<lambda(int, int)> >]’
/opt/rh/devtoolset-10/root/usr/include/c++/10/tuple:1678:63:   required from ‘constexpr std::pair<_T1, _T2>::pair(std::piecewise_construct_t, std::tuple<_Args1 ...>, std::tuple<_Args2 ...>) [with _Args1 = {int&&}; _Args2 = {}; _T1 = const int; _T2 = std::__debug::set<int, main()::<lambda(int, int)> >]’
/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_construct.h:97:14:   required from ‘constexpr decltype (::new(void*(0)) _Tp) std::construct_at(_Tp*, _Args&& ...) [with _Tp = std::pair<const int, std::__debug::set<int, main()::<lambda(int, int)> > >; _Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; decltype (::new(void*(0)) _Tp) = std::pair<const int, std::__debug::set<int, main()::<lambda(int, int)> > >*]’
/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/alloc_traits.h:514:4:   required from ‘static constexpr void std::allocator_traits<std::allocator<_CharT> >::construct(std::allocator_traits<std::allocator<_CharT> >::allocator_type&, _Up*, _Args&& ...) [with _Up = std::pair<const int, std::__debug::set<int, main()::<lambda(int, int)> > >; _Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; _Tp = std::_Rb_tree_node<std::pair<const int, std::__debug::set<int, main()::<lambda(int, int)> > > >; std::allocator_traits<std::allocator<_CharT> >::allocator_type = std::allocator<std::_Rb_tree_node<std::pair<const int, std::__debug::set<int, main()::<lambda(int, int)> > > > >]’
/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_tree.h:618:32:   required from ‘void std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_construct_node(std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Link_type, _Args&& ...) [with _Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; _Key = int; _Val = std::pair<const int, std::__debug::set<int, main()::<lambda(int, int)> > >; _KeyOfValue = std::_Select1st<std::pair<const int, std::__debug::set<int, main()::<lambda(int, int)> > > >; _Compare = std::less<int>; _Alloc = std::allocator<std::pair<const int, std::__debug::set<int, main()::<lambda(int, int)> > > >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Link_type = std::_Rb_tree_node<std::pair<const int, std::__debug::set<int, main()::<lambda(int, int)> > > >*]’
/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_tree.h:635:21:   required from ‘std::_Rb_tree_node<_Val>* std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_create_node(_Args&& ...) [with _Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; _Key = int; _Val = std::pair<const int, std::__debug::set<int, main()::<lambda(int, int)> > >; _KeyOfValue = std::_Select1st<std::pair<const int, std::__debug::set<int, main()::<lambda(int, int)> > > >; _Compare = std::less<int>; _Alloc = std::allocator<std::pair<const int, std::__debug::set<int, main()::<lambda(int, int)> > > >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Link_type = std::_Rb_tree_node<std::pair<const int, std::__debug::set<int, main()::<lambda(int, int)> > > >*]’
/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_tree.h:2461:33:   required from ‘std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_emplace_hint_unique(std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::const_iterator, _Args&& ...) [with _Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; _Key = int; _Val = std::pair<const int, std::__debug::set<int, main()::<lambda(int, int)> > >; _KeyOfValue = std::_Select1st<std::pair<const int, std::__debug::set<int, main()::<lambda(int, int)> > > >; _Compare = std::less<int>; _Alloc = std::allocator<std::pair<const int, std::__debug::set<int, main()::<lambda(int, int)> > > >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator = std::_Rb_tree<int, std::pair<const int, std::__debug::set<int, main()::<lambda(int, int)> > >, std::_Select1st<std::pair<const int, std::__debug::set<int, main()::<lambda(int, int)> > > >, std::less<int>, std::allocator<std::pair<const int, std::__debug::set<int, main()::<lambda(int, int)> > > > >::iterator; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::const_iterator = std::_Rb_tree<int, std::pair<const int, std::__debug::set<int, main()::<lambda(int, int)> > >, std::_Select1st<std::pair<const int, std::__debug::set<int, main()::<lambda(int, int)> > > >, std::less<int>, std::allocator<std::pair<const int, std::__debug::set<int, main()::<lambda(int, int)> > > > >::const_iterator]’
/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_map.h:520:37:   required from ‘std::__cxx1998::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::__cxx1998::map<_Key, _Tp, _Compare, _Alloc>::operator[](std::__cxx1998::map<_Key, _Tp, _Compare, _Alloc>::key_type&&) [with _Key = int; _Tp = std::__debug::set<int, main()::<lambda(int, int)> >; _Compare = std::less<int>; _Alloc = std::allocator<std::pair<const int, std::__debug::set<int, main()::<lambda(int, int)> > > >; std::__cxx1998::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = std::__debug::set<int, main()::<lambda(int, int)> >; std::__cxx1998::map<_Key, _Tp, _Compare, _Alloc>::key_type = int]’
main.cpp:339:9:   required from here
/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_tree.h:149:24: error: use of deleted function ‘main()::<lambda(int, int)>::<lambda>()’
  149 |       : _M_key_compare()
      |                        ^
main.cpp:333:16: note: a lambda closure type has a deleted default constructor
  333 |     auto cmp=[&](int i, int j){
      |                ^

compiler option:

-std=c++20 -O2 -Wall -Wextra -Wshadow -Wconversion -Wfloat-equal -Wduplicated-cond -Wlogical-op -Wall -Wextra -O2 -Wlogical-op -Wshift-overflow -Wduplicated-cond -Wcast-qual -Wcast-align -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC -D_FORTIFY_SOURCE=2 -fno-sanitize-recover -fstack-protector

I also tried std::unordered_map<int,std::set<int,cmp>>mp; but it gave:

main.cpp:338:21: error: type/value mismatch at argument 2 in template parameter list for ‘template<class _Key, class _Cmp, class _Allocator> class std::__debug::set’
  338 |     map<int,set<int,cmp>>mp;
      |                     ^~~
main.cpp:338:21: note:   expected a type, got ‘cmp’
main.cpp:338:24: error: template argument 2 is invalid
  338 |     map<int,set<int,cmp>>mp;
      |                        ^~
main.cpp:338:24: error: template argument 4 is invalid
main.cpp:339:7: error: invalid types ‘int[int]’ for array subscript
  339 |     mp[0].insert(2);

Is there a way to achieve this? I looked into this but it didn't helped.


Solution

  • You cannot use operator[] of your map, because it needs to default-construct your set. And you cannot default-construct std::set<int, decltype(cmp)> mySet;, because your cannot default-construct an instance of your capturing lambda.

    Instead of operator[] you can use insert:

    mp.insert(std::make_pair(0, std::set<int, decltype(cmp)>{cmp}));
    mp.at(0).insert(2);
    

    Note however that the logic of your comparator might be broken, e.g. it will cause Undefined Behavior if you insert an element that is bigger/equal to the size of your wordsContainer.