使用无捕获 lambda 表达式作为条件运算符的第二个和第三个操作数时出现 MSVC 错误
MSVC error when using capture-less lambda expressions as second and third operand of conditional operator
下面的代码被 GCC 和 Clang 愉快地接受 -std=c++14
但导致编译错误 Visual Studio 2013.
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
auto increasing = [](int lhs, int rhs){return lhs < rhs;};
auto decreasing = [](int lhs, int rhs){return lhs > rhs;};
std::vector<int> 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::<lambda_0228ee097b83254cfd8aa5f4015a245b>' to 'main::<lambda_cb3b816d067baa9d4462132ee332363c>'
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] error in assigning lambda expr though "operator?:" while capturing 涵盖了这个并说:
The compiler behaviour looks correct to me. The difference of the
lambda expressions in bar and foo3
compared to the other two is that
these are capture-free lambdas and thus have a conversion function to
function pointer.
Each lambda expression corresponds to a unique class type, so what we
have in foo1
and foo2
can be compared with the following
class-example:
struct A{}; struct B{};
void f() { false ? A() : B(); }
This expression has no common type for the conditional operator and is
ill-formed.
What we have in bar
and foo3
can be compared with the following
class-example :
struct A
{
typedef void (*F)();
operator F();
};
struct B
{
typedef void (*F)();
operator F();
};
void f() { false ? A() : B(); }
This is well-formed, because in the last step of the conditional
operator conversion attempts (5.16p5), more general conversions are
attempted and these find the common pointer to function.
5.16p5
说:
Otherwise, the result is a prvalue. If the second and third operands
do not have the same type, and either has (possibly cv-qualified)
class type, overload resolution is used to determine the conversions
(if any) to be applied to the operands (13.3.1.2, 13.6). If the
overload resolution fails, the program is ill-formed. Otherwise, the
conversions thus determined are applied, and the converted operands
are used in place of the original operands for the remainder of this
section.
如果我们按如下方式更改您的代码:
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
表示 (see it live):
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);
^ ~~~~~~~~~~ ~~~~~~~~~~
参考 draft C++11 standard 5.1.2
[expr.prim.lambda] 说:
The closure type for a lambda-expression with no lambda-capture has a
public non-virtual non-explicit const conversion function to pointer
to function having the same parameter and return types as the closure
type’s function call operator. The value returned by this conversion
function shall be the address of a function that, when invoked, has
the same effect as invoking the closure type’s function call operator.
措辞在 C++14 标准草案中有所修改,但不会改变 属性。
更新
提交了 bug report.
下面的代码被 GCC 和 Clang 愉快地接受 -std=c++14
但导致编译错误 Visual Studio 2013.
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
auto increasing = [](int lhs, int rhs){return lhs < rhs;};
auto decreasing = [](int lhs, int rhs){return lhs > rhs;};
std::vector<int> 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::<lambda_0228ee097b83254cfd8aa5f4015a245b>' to 'main::<lambda_cb3b816d067baa9d4462132ee332363c>'
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] error in assigning lambda expr though "operator?:" while capturing 涵盖了这个并说:
The compiler behaviour looks correct to me. The difference of the lambda expressions in bar and
foo3
compared to the other two is that these are capture-free lambdas and thus have a conversion function to function pointer.Each lambda expression corresponds to a unique class type, so what we have in
foo1
andfoo2
can be compared with the following class-example:struct A{}; struct B{}; void f() { false ? A() : B(); }
This expression has no common type for the conditional operator and is ill-formed.
What we have in
bar
andfoo3
can be compared with the following class-example :struct A { typedef void (*F)(); operator F(); }; struct B { typedef void (*F)(); operator F(); }; void f() { false ? A() : B(); }
This is well-formed, because in the last step of the conditional operator conversion attempts (5.16p5), more general conversions are attempted and these find the common pointer to function.
5.16p5
说:
Otherwise, the result is a prvalue. If the second and third operands do not have the same type, and either has (possibly cv-qualified) class type, overload resolution is used to determine the conversions (if any) to be applied to the operands (13.3.1.2, 13.6). If the overload resolution fails, the program is ill-formed. Otherwise, the conversions thus determined are applied, and the converted operands are used in place of the original operands for the remainder of this section.
如果我们按如下方式更改您的代码:
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
表示 (see it live):
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);
^ ~~~~~~~~~~ ~~~~~~~~~~
参考 draft C++11 standard 5.1.2
[expr.prim.lambda] 说:
The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function having the same parameter and return types as the closure type’s function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type’s function call operator.
措辞在 C++14 标准草案中有所修改,但不会改变 属性。
更新
提交了 bug report.