VS2010 中 C++ lambda 表达式的奇怪错误(变量 y1 )

Weird bug with C++ lambda expressions in VS2010 ( variable y1 )

本文关键字:错误 变量 y1 C++ lambda 表达式 VS2010      更新时间:2023-10-16

在我的几个项目中,以下代码:

#include <functional>
class SmallClass
{
public:
int x1, y1;
void TestFunc()
{
auto BadLambda = [&]()
{
int g = x1 + 1; //ok
int h = y1 + 1; //c2296
int l = static_cast<int>(y1); //c2440
};
int y1_copy = y1; //it works if you create a local copy
auto GoodLambda = [&]()
{
int h = y1_copy + 1; //ok
int l = this->y1 + 1; //ok
};
}
};

生成

错误 C2296:"+":非法的左操作数类型为"双精度 (__cdecl *)(双倍)'

或者

错误 C2440:"static_cast":无法从"双精度 (__cdecl *)(双精度)' 到 'int'

你明白了。如果按值捕获,也会发生这种情况。

该错误似乎与成员名称"y1"有关。它发生在不同的类,不同的项目中,并且(似乎)y1的任何类型的类型;例如,此代码:

[...]
MyClass y1;
void TestFunc()
{
auto BadLambda = [&]()->void
{
int l = static_cast<int>(y1); //c2440
};
}

生成以下两个错误:

错误 C2440:"static_cast":无法从"MyClass"转换为"int"否 提供可执行此操作的用户定义转换运算符 转换,或者无法调用运算符

错误 C2440:"static_cast":无法从"双精度 (__cdecl *)(双倍)' 到 'int' 没有上下文可以进行这种转换

它似乎链接到"功能"库。它发生在(在我的机器上)一个仅包含"功能"的最小项目中(是的,它应该在括号之间,但我在 HTML 上失败了)。

这似乎不是一个已知的错误,我不知所措。关于为什么会发生这种情况的任何想法?(我不需要解决方法;代码中已经有一些了)。

编辑:它确实与math.h的函数有关:

_CRT_NONSTDC_DEPRECATE(_cabs) _CRTIMP double __cdecl cabs(Instruct _complex _X); _CRT_NONSTDC_DEPRECATE(_j0) _CRTIMP double __cdecl j0(Indouble _X); _CRT_NONSTDC_DEPRECATE(_j1) _CRTIMP double __cdecl j1(Indouble _X); _CRT_NONSTDC_DEPRECATE(_jn) _CRTIMP double __cdecl jn(Inint _X,Indouble _Y); _CRT_NONSTDC_DEPRECATE(_y0) _CRTIMP double __cdecl y0(Indouble _X); _CRT_NONSTDC_DEPRECATE(_y1) _CRTIMP double __cdecl y1(Indouble _X); _CRT_NONSTDC_DEPRECATE(_yn) _CRTIMP double __cdecl yn(Inint _X,Indouble _Y);

使用这些函数名称中的任何一个都会触发错误。当包括math.h,cmath或函数时,就会发生这种情况。也许有人知道这些名字是如何进入我的 lambda 表达式范围的?

编辑:已解决。这是VS2010(可能还有其他较旧的编译器)中的lambda名称解析问题。如果定义全局名称或使用"使用命名空间 x",请避免在 lambda 中使用非限定名称。

使用 Visual Studio 2010 Express 版本 10.0.40219.1 SP1Rel。

y1的使用与<cmath>中的y1冲突(第二种贝塞尔函数,阶数 1)。这就是为什么std::命名空间是一件好事(sm)以及为什么不应该用using namespace std;来规避它。(不过,恕我直言,using std::string很好。当然,并非所有C++cmath标头都正确地将所有名称仅放置在命名空间std中,但它们应该;数学库中的短名称太多。

C++名称解析规则很复杂,我不会假装理解所有尘土飞扬的小角落。lambda 中的非限定名称的查找方式很可能与成员函数实际主体中的非限定名称的查找方式不同。在成员函数之外,非限定名只能引用已声明的类成员。(不过,this->y1并不是一个不合格的名称。


编辑:事实证明,这是VC10中的一个错误,它在lambda中应用了不正确的名称解析规则。lambda 表达式中的名称解析错误被多次报告,但这些错误在 VC12 中被标记为已修复(话虽如此,我不知道如何找到未标记为已修复的错误报告)。我坚持下面的建议,尽管关于使用显式this->的建议可能更具争议性,但是我已经被意外的名称查找烧伤了几次,显式限定可以提供帮助。


总的来说,我的建议是:

1)切勿使用using namespace std;

2)如果这是你的意思,请始终使用this->。(或者,至少对类数据成员使用尾随_约定。


编辑C 库标头对std命名空间的使用。

17.6.1.2(4):但是,在C++标准库中,声明(在 C 中定义为宏的名称除外)在命名空间 std 的命名空间范围 (3.3.6) 内。未指定这些名称是否首先在全局命名空间范围内声明,然后通过显式 using 声明 (7.3.3) 注入到命名空间 std 中。

换句话说,名称必须位于std::中,并且可能位于全局命名空间中。因此,始终如一地使用std::并没有什么坏处。它可能没有帮助,但它也可能保护您免受将来某些标准库的侵害,这些标准库不会将这些名称注入全局命名空间。

老实说,我不明白从 int 到 int 转换的意义。你可以简单地写:

int l = y1;

但是如果你真的想将 int 转换为 int,下面的代码应该可以工作。

int l = static_cast<int>(y1); 

该类在我的VS2010上编译,我只编辑了一行:int l = static_cast(y1); //c2440

已更改为:int l = static_cast<int>(y1);

虽然这是一个看似很酷的错误,但请尝试使用一个好的约定来命名变量。使它们具有描述性,但不要太长。阅读 Rob Pike 和 Kernighan 的《编程实践》一书,以更好地了解命名变量和使用一致的样式和格式。

编辑:

#include <cmath>using namespace std;.我没有收到任何错误,但是如果我创建一个class MyClass{};并创建一个对象MyClass y1,我会收到一个错误,指出"..previous definition 'function'". 类SimpleClass内部的y1永远不会受到影响。