mercredi 26 août 2015

Using reference-to-lambda as comparator in a map (the right way?)

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.

Aucun commentaire:

Enregistrer un commentaire