Я хочу использовать лямбда как пользовательский компаратор в std::map
, но, к сожалению, компилятор Visual Studio 2013 не позволяет использовать простой код:
auto cmp = [](int l, int r) { return l < r; };
std::map<int, int, decltype(cmp)> myMap(cmp);
myMap[1] = 1;
и с ошибкой
ошибка C3497: вы не можете создать экземпляр lambda
Кажется, что этот код отлично работает в GCC 5.1 и Visual Studio 2015 (проверено с помощью ideone и онлайн-компилятор VС++). Но для VS2013 одним из решений было бы использовать ссылку, предложенную здесь (примечание auto &):
auto& cmp = [](int l, int r) { return l < r; };
std::map<int, int, decltype(cmp)> myMap(cmp);
myMap[1] = 1;
Очевидно, что GCC не компилирует это из-за привязки неконстантной ссылки на временную, в то время как VS2015 выдает предупреждение об использовании нестандартного расширения. Вместо этого можно использовать ссылку на константу, но тогда следующий код не будет компилироваться (обратите внимание на изменчивый - я немного растягиваю его с помощью компаратора с состоянием):
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;
Итак, я вижу два способа обойти это, в то же время имея код, совместимый с VS2013. Во-первых,
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;
Но это заставляет меня задуматься о том, как Стефан Т. Лававей говорит о том, как передавать исходные ссылки, поскольку явные параметры шаблона могут быть неправильными, если внутренне они используются в контексте вывода типа шаблона - он говорит об этом именно в этот момент в его презентации.
Другой подход заключается в использовании 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;
Итак, наконец, мой вопрос: гарантируется ли это каким-либо образом, что передача ссылочного типа в качестве компаратора безопасна? Или это зависит от исполнителей STL, и в некоторых случаях это может сломаться, и поэтому использование reference_wrapper
- это способ пойти?
Последнее замечание: я думаю, что передача ссылки (в любой форме) может оказаться полезной вне мира VS2013, если по какой-то причине не требуется копировать компаратор.
Cheers, Ростислав.
Изменить: Еще одно отличие:
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;