I want to use a lambda as a custom comparator in a std::map
but unfortunately Visual Studio 2013 compiler does not allow using a simple code like that:
auto cmp = [](int l, int r) { return l < r; };
std::map<int, int, decltype(cmp)> myMap(cmp);
myMap[1] = 1;
and fails with
error C3497: you cannot construct an instance of a lambda
Seems that this code works fine in GCC 5.1 and Visual Studio 2015 (checked using ideone and VC++ online compiler). But for VS2013, one of the solutions would be to use a reference as proposed here (note auto&):
auto& cmp = [](int l, int r) { return l < r; };
std::map<int, int, decltype(cmp)> myMap(cmp);
myMap[1] = 1;
Clearly, GCC doesn't compile this due to binding a non-const reference to a temporary, while VS2015 issues a warning about the use of a non-standard extension. One could also use a const reference instead, but then the following code will not compile (note mutable - I'm stretching it a little by having a stateful comparator):
int compCounter = 0;
const auto& cmp = [&compCounter](int l, int r) mutable { ++compCounter; return l < r; };
std::map<int, int, decltype(cmp)> myMap(cmp);
myMap[1] = 1;
So, I see two ways of getting around this, while at the same time having the code compatible with VS2013. First,
int compCounter = 0;
auto cmp = [&compCounter](int l, int r) mutable { ++compCounter; return l < r; };
std::map<int, int, decltype(cmp)&> myMap(cmp);
myMap[1] = 1;
But this makes me think about Stephan T. Lavavej's talk on how passing raw references as explicit template parameters might be wrong if internally it is used in a template type deduction context - he talks about this at exactly this point in his presentation.
The other approach is to use a std::reference_wrapper
:
int compCounter = 0;
auto cmp = [&compCounter](int l, int r) mutable { ++compCounter; return l < r; };
std::map<int, int, std::reference_wrapper<decltype(cmp)>> myMap(cmp);
myMap[1] = 1;
So my question finally is: is it guaranteed by any means that passing a raw reference type as comparator is safe? Or it depends on the STL implementers and it might break in some case and, therefore, using reference_wrapper
is the way to go?
One final note: I think that passing a reference (in any form) might be useful outside of VS2013 world in case for some reason one doesn't want the comparator to be copied.
Cheers, Rostislav.
Edit: One more difference:
int compCounter = 0;
auto cmp = [&compCounter](int l, int r) mutable { ++compCounter; return l < r; };
//using cmpT = decltype(cmp)&;
using cmpT = std::reference_wrapper<decltype(cmp)>;
std::map<int, int, cmpT> myMap(cmp);
myMap[1] = 1;
// Will work in both cases of cmpT
std::map<int, int, cmpT> m2(myMap);
// Will work only for reference_wrapper
std::map<int, int, cmpT> m2(cmp);
m2 = myMap;