lambda 如何在 MSVC2017 15.9.3 中使用 /std:c++17 中的静态局部错误返回值?
Posted
技术标签:
【中文标题】lambda 如何在 MSVC2017 15.9.3 中使用 /std:c++17 中的静态局部错误返回值?【英文标题】:How is value returned by lambda using a static local wrong in MSVC2017 15.9.3 with /std:c++17? 【发布时间】:2018-12-06 22:21:46 【问题描述】:下面的示例代码打印来自 lambda 函数的值,该函数简单地递增并返回静态局部计数器变量的值。
它打印0,1
和2,3
与 gcc 和 C++17 的预期一致。但不是在 Visual Studio Community 2017 15.9.3 中设置了/std:c++17
- 它会打印0,0
和2,3
。
#include <iostream>
int main()
auto f = []
static int i = 0;
return i++;
;
const int v1 = f(); // Expect v1 = 0
const int v2 = f(); // Expect v2 = 1
// Prints the wrong values (MSVC 15.9.3 with /std:c++17)
std::cout << v1 << "," << v2 << std::endl; // Expect "0,1", prints "0,0"
// Prints the right values (or ought to with C++17 sequencing, anyway)
std::cout << f() << "," << f() << std::endl; // Expect "2,3", prints "2,3"
return 0;
奇怪的输出(在 x86 调试版本中)
0,0
2,3
它看起来像一个编译器错误(所以我们提交了报告):https://developercommunity.visualstudio.com/content/problem/347419/unexpected-return-from-lambda-with-static-local-va.html
生成的程序有什么问题,以至于它错误地为v1
和v2
打印0
,但之后正确打印2, 3
?任何有根据的猜测编译器错误是什么?
作为一种解决方法,我改用了捕获:
auto f = [i = 0]() mutable
return i++;
;
更新 - 作为旁注,上面示例的输出在 x86 版本构建中再次不同:
0,1
3,2
MSVC 存在另一个问题,尽管设置了/std:c++17
,但std::cout
的<<
运算符没有从左到右排序,我推测这会导致3,2
在这里输出,在最少。
【问题讨论】:
你真的认为我们可以解释闭源编译器的内部工作原理吗? 两次输入函数不是更好吗? 真正奇怪的是,如果您在第一行设置断点cout
自动监视窗口显示 v1
和 v2
分别是 0
和 1
。
查看程序集告诉我们的是,是的,它做错了。
@aschepler 好点,我稍微改述了这个问题。
【参考方案1】:
MSVC 干净地编译以下内容:
constexpr int foo()
static int i = 0;
return i++;
static_assert(foo() == foo()); // oh no
这不符合标准。
所以,从 C++17 开始,如果可以的话,lambdas 隐含地是 constexpr
。 MSVC 错误地认为 lambda 是constexpr
,因此将f()
折叠成v2
的常量(它来自v1
)。当您直接输出它时它不会这样做,因为它显然不会像 gcc 那样急切地评估 constexpr
的东西(或使用我们不知道的其他一些启发式方法)。
【讨论】:
谢谢。我注意到从v1
和 v2
中删除 const
会使问题消失,如果这样做会使编译器决定不折叠 lambda。
@SamTwidale Jup,它们没有被折叠,因为它们不是const
(也不是constexpr
)。
我刚刚注意到别的东西,也许这是我的错误还是又是 MSVC?在 x86 版本中,示例在我的 PC 上输出 0,1
和 3,2
,尽管我认为 std::cout 的 couts 更改为打印v1
和v2
并通过单独调用对f
进行两次调用,它会正确输出0,1
和2,3
。
@SamTwidale 即使v1
和v2
是const
?。但是,是的,这是 MSVC 的一个已知错误。
@SamTwidale 不要这么认为。可能再次与不断折叠有关。以上是关于lambda 如何在 MSVC2017 15.9.3 中使用 /std:c++17 中的静态局部错误返回值?的主要内容,如果未能解决你的问题,请参考以下文章
如果我的模板类型首先作为 lambda 参数出现,MSVC 会引发一个奇怪的错误
与 GCC/MSVC 中的 lambda 转换构造函数的差异
MSVC++:模板的 static_assert 不会在 lambda 内触发
传入 Lambda 时,Visual Studio 2017 中的哪些扩展可以消除“bool”与“std::function”的歧义?