4

I'm trying to write a function to return a std::set with a custom comparator (per the advice from this answer), as follows:

#include <iostream>
#include <set>

auto GetSet()
{
    const auto cmp = [](auto n1, auto n2) { return n1 < n2; };
    std::set<int, decltype(cmp)> mySet(cmp); // compiler error, see below
    mySet.insert(13);
    mySet.insert(31);
    return mySet;
}
int main()
{
    auto mySet = GetSet();
    for (auto i : mySet)
        std::cout << i << " ";
}

Obviously this is for demonstration purposes, my class is more complex than an int

It works fine in GCC Coliru link, but does not work in VS2019. In VS2019 (using /std:c++17) it produces the following error:

Error C2783 'void std::swap(_Ty &,_Ty &) noexcept()': could not deduce template argument for '_Enabled' C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\utility 114

If I change the code to not use the function, and instead have:

int main()
{
    const auto cmp = [](auto n1, auto n2) { return n1 < n2; };
    std::set<int, decltype(cmp)> mySet(cmp);
    mySet.insert(13);
    mySet.insert(31);
    for (auto i : mySet)
        std::cout << i << " ";
}

It works correctly. Is there anything wrong with the above, or is Microsoft being more pedantic, or some kind of compiler bug?

Tas
  • 7,023
  • 3
  • 36
  • 51
  • 1
    It's the library being too restrictive, I think. MS's library wants the lambda to have an assignment operator, which a) isn't happening since you instantiated `std::set` at the `const` version of the lambda's type, b) is implicitly `delete`d for closure types until C++20, and c) shouldn't be necessary, as a (admittedly cursory) look at cppreference shows no requirement that it be either copy or move assignable (it does require copy constructibility). – HTNW Sep 14 '20 at 04:26
  • @JaMiT my apologies, I created a test VS project and it seems it defaulted to C++14. I changed it to be C++17 and it still fails (this is expected because the project I was using was C++17, I just created a test application _just in case_), but I've updated the question with the error message from c++17 – Tas Sep 14 '20 at 04:28
  • Probably my naivete but it seems interesting that it succeeds when the deduction guide is used `std::set mySet{ { 13, 31 }, cmp };`, under MSVC 2019 – Aluan Haddad Sep 14 '20 at 04:51
  • Probably line ```auto mySet = GetSet();``` wants to deduce the ```mySet``` type first, but it can not, since it does not know nothing about the lambda comparator. – dgrandm Sep 14 '20 at 05:05
  • 2
    @dgrandm the type of the comparator is deduced from the return type deduced for `auto GetSet() {...}` – Aluan Haddad Sep 14 '20 at 05:20
  • the answer you linked says that this is for C++20, not C++17 – phuclv Sep 14 '20 at 06:56
  • @phuclv no the answer says that in C++20 `std::set s` would be fine, whereas I have `std::set s(cmp)`, which is under the "modern C++11 solution" – Tas Sep 14 '20 at 07:55

1 Answers1

0

I don't have an answer on what's going wrong (whether GCC is being sloppy or MSVC is being more pedantic), but you can use the following instead:

#include <set>
#include <functional>
auto GetSet()
{
    const auto cmp = [](auto n1, auto n2) { return n1 < n2; };
    std::set<int, std::function<bool(int, int)>> mySet(cmp);
    mySet.insert(13);
    mySet.insert(31);
    return mySet;
}

Which works on both compilers.

Tas
  • 7,023
  • 3
  • 36
  • 51