【问题标题】:MSVC error when using capture-less lambda expressions as second and third operand of conditional operator使用无捕获 lambda 表达式作为条件运算符的第二个和第三个操作数时出现 MSVC 错误
【发布时间】:2015-03-15 08:34:26
【问题描述】:

下面的代码很高兴被 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,并且标准中是否有明确处理这种情况的部分?

【问题讨论】:

  • 请务必提交bug report
  • @ShafikYaghmour 当我尝试这样做时,我收到“您无权提交此连接的反馈”。所以我想我不允许报告错误。
  • 嗯,不久前我创建了我的连接帐户,我认为我不需要做任何特别的事情来报告错误。
  • 我为此提交了一个错误报告,希望我有办法使用较新版本的 Visual Studio 进行确认。
  • 我有 Visual Studio Community 2017,它显示一个错误(运算符的多个定义可用),但它运行得很好。

标签: c++ visual-c++ c++11 visual-studio-2013 language-lawyer


【解决方案1】:

由于 lambda 捕获它们都不能转换为具有兼容签名的函数指针,因此 gccclang 在这里是正确的。

有一个 gcc 错误报告很好地总结了这个主题: [c++ lambda] error in assigning lambda expr though "operator?:" while capturing 涵盖了这个并说:

编译器的行为对我来说是正确的。的区别 bar 和 foo3 中的 lambda 表达式与其他两个相比是 这些是无捕获的 lambda,因此具有转换功能 函数指针。

每个 lambda 表达式对应一个唯一的类类型,所以我们 在foo1foo2 中有可以和下面的比较 类示例:

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

此表达式没有条件运算符的通用类型,并且是 格式不正确。

我们在barfoo3 中的内容可以与以下内容进行比较 类示例:

struct A
{
    typedef void (*F)();
    operator F();
};

struct B
{
    typedef void (*F)();
    operator F();
};

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

这是格式正确的,因为在条件的最后一步 运算符转换尝试(5.16p5),更一般的转换是 尝试过,它们找到了指向函数的公共指针。

5.16p5 说:

否则,结果为纯右值。如果第二个和第三个操作数 没有相同的类型,并且有(可能是 cv-qualified) 类类型,重载决议用于确定转换 (如果有)应用于操作数(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;};

gccclang 都会产生错误,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 standard5.1.2[expr.prim.lambda] 说:

没有 lambda 捕获的 lambda 表达式的闭包类型有 公共非虚拟非显式 const 转换函数到指针 函数具有与闭包相同的参数和返回类型 类型的函数调用运算符。此转换返回的值 function 应该是一个函数的地址,当它被调用时,有 与调用闭包类型的函数调用运算符的效果相同。

C++14 标准草案中的措辞有所修改,但并未改变此属性。

更新

提交了bug report

【讨论】:

  • 即使使用“-std=c++11”,Clang 也接受代码有点奇怪。我确实认为它不应该将没有捕获的 lambda 转换为函数指针是 C++14 标准。
  • @Windoze 来自 C++11“国际标准”文档:“没有 lambda 捕获的 lambda 表达式的闭包类型具有公共的非虚拟非显式 const 转换函数指向函数 [...]"
  • @Windoze 我将此转换的标准参考添加到我的答案中。
猜你喜欢
  • 2013-09-11
  • 1970-01-01
  • 2012-03-28
  • 1970-01-01
  • 2016-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-13
相关资源
最近更新 更多