Ошибка MSVC при использовании неактивных lambda-выражений в качестве второго и третьего операндов условного оператора

Приведенный ниже код с радостью принимается как GCC, так и Clang с -std=c++14 но вызывает ошибку компиляции с Visual Studio 2013.

 #include  #include  #include  using namespace std; int main() { auto increasing = [](int lhs, int rhs){return lhs  rhs;}; std::vector v(0, 10); bool increase = true; std::sort(v.begin(), v.end(), increase ? increasing : decreasing); return 0; } 

Ошибка:

main.cpp(11): error C2446: ':': no conversion from 'main::' to 'main::' main.cpp(11): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

Я предполагаю, что мой вопрос в том, какой компилятор здесь совместим, я предполагаю, что это не MSVC, и есть ли часть стандарта, который явно касается этой ситуации?

Поскольку ни lambda-захват, они не могут быть преобразованы в указатели функций с совместимыми сигнатурами, поэтому gcc и clang здесь верны.

Существует отчет об ошибке gcc, в котором очень хорошо излагается этот вопрос: ошибка [c ++ lambda] при присвоении lambda expr хотя «operator ?:» при захвате, которая охватывает это, и говорит:

Поведение компилятора выглядит корректно для меня. Разница lambda-выражений в барах и foo3 по сравнению с двумя другими заключается в том, что они являются свободными от захвата lambda и, следовательно, имеют функцию преобразования для функции указателя.

Каждое lambda-выражение соответствует уникальному типу classа, поэтому то, что мы имеем в foo1 и foo2 можно сравнить со следующим примером classа:

 struct A{}; struct B{}; void f() { false ? A() : B(); } 

Это выражение не имеет общего типа для условного оператора и плохо сформировано.

То, что мы имеем в bar и foo3 можно сравнить со следующим примером classа:

 struct A { typedef void (*F)(); operator F(); }; struct B { typedef void (*F)(); operator F(); }; void f() { false ? A() : B(); } 

Это хорошо сформировано, потому что на последнем этапе попыток преобразования условного оператора (5.16p5) предпринимаются попытки более общего преобразования, и они находят общий указатель на функцию.

5.16p5 говорит:

В противном случае результатом будет prvalue. Если второй и третий операнды не имеют одного и того же типа и имеют (возможно, cv-qualit) тип classа, то для определения конверсий (если они есть) применяются к операндам (13.3.1.2, 13.6) , Если разрешение перегрузки выходит из строя, программа плохо сформирована. В противном случае применяются указанные таким образом преобразования, а преобразованные операнды используются вместо исходных операндов для остальной части этого раздела.

Если мы изменим ваш код следующим образом:

 int x = 20 ; auto increasing = [&x](int lhs, int rhs){return lhs < rhs;}; auto decreasing = [&x](int lhs, int rhs){return lhs > rhs;}; 

и gcc и clang генерируют ошибку, говорит clang ( см. это в прямом эфире ):

 error: incompatible operand types ('(lambda at prog.cc:8:23)' and '(lambda at prog.cc:9:23)') std::sort(v.begin(), v.end(), increase ? increasing : decreasing); ^ ~~~~~~~~~~ ~~~~~~~~~~ 

Для справки проект стандарта C ++ 11 5.1.2 [expr.prim.lambda] гласит:

Тип замыкания для lambda-выражения без lambda-захвата имеет публичную не виртуальную неявную функцию преобразования const для указателя на функцию, имеющую тот же параметр и возвращаемые типы, что и оператор вызова функции типа закрытия. Значение, возвращаемое этой функцией преобразования, должно быть адресом функции, которая при вызове имеет тот же эффект, что и вызов оператора вызова типа замыкания.

Формулировка изменена в проекте стандарта C ++ 14, но не изменяет это свойство.

Обновить

Подал отчет об ошибке .