MSVC 引发“运行时检查失败 #2”(损坏的堆栈)以使用重载 / std::visit 模式 (C++17)

MSVC throws "Run-Time Check Failure #2" (corrupted stack) for this use of the overload / std::visit pattern (C++17)

提问人:PCB 提问时间:11/7/2023 更新时间:11/9/2023 访问量:128

问:

我正在使用“重载”模式来使 std::variant 成为访问者,将方法定义为具有通用 lambda 的 lambda 以提供空的默认方法。(稍后完整示例代码)

// imagine declaration of the overload template + the deduction helper here ...

using Foodstuff = std::variant<Fruit, Vegetable, Meat, Dairy, Fish>;

int things_eaten{0};

auto picky_eater = overload{
    // Eats fruit
    [&things_eaten](const Fruit&) { things_eaten++; },
    // Eats dairy
    [&things_eaten](const Dairy&) {things_eaten++; },

    // Doesn't eat anything else - I need an empty handler for the other foodstuffs
    [](auto) {}
};

然后我用它来做食物的载体。在 MSVC 2022 (v17.7.6) 中编译它,针对 C++17 语言标准,我没有收到编译器警告(在 /W3),并且程序在“发布”目标中按我的预期运行,但是当它退出函数时,我将其更改为调试,我得到异常std::visit

运行时检查失败 #2 - 围绕变量“picky_eater”堆叠 已损坏。

请注意,其他 lambda 具有捕获,而最终的通用 lambda 没有,如果我将最后一个重载更改为 ,则该异常将消失。[&things_eaten](auto) {}

此外,如果我将语言标准更改为 C++20,原始版本不会生成该异常。

异常是否识别了原始代码中的真正问题,C++20 是否引入了解决它的东西(也许是模板推导更改?这是调试 buuilds 中的 MSVC C++17 运行时检查中的一个奇怪的错误(我没有其他编译器或分析工具可以比较)。


#include <variant> 
#include <vector>
#include <string>
#include <iostream>

// Overload pattern

template <typename... Ts>
struct overload : Ts...
{
    using Ts::operator()...;
};

// Template deduction helper; needed in C++17
template <typename... Ts>
overload(Ts...)->overload<Ts...>;

///////////////////////////////////////////////////////////

struct Fruit {
    std::string name;
};

struct Vegetable {
    std::string name;
};

struct Meat {
    std::string name;
};

struct Dairy {
    std::string name;
};

struct Fish {
    std::string name;
};

using Foodstuff = std::variant<Fruit, Vegetable, Meat, Dairy, Fish>;
using Meal = std::vector<Foodstuff>;

int main() {
    auto meal = Meal{ Fruit{"Apple"}, Vegetable{"Tomato"}, Meat{"Beef"}, Vegetable{"Potato"}, Dairy{"Yoghurt"} };

    int things_eaten{ 0 };

    auto picky_eater = overload{
        // Eats fruit
        [&things_eaten](const Fruit&) { things_eaten++; },

        // Eats dairy
        [&things_eaten](const Dairy&) { things_eaten++; },

        // Doesn't eat anything else - I need an empty handler for the other foodstuffs

        // If I write the generic lambda this way, MSVC in C++17 langauge standard will
        // give an exception in Debug builds:
        // "Run-Time Check Failure #2 - Stack around the variable 'picky_eater' was corrupted."
        [](auto) {}

        // But if I just add the same capture, even though it's not used, that error goes away?
        // [&things_eaten](auto) {}
    };

    for (const auto& ingredient : meal) {
        std::visit(picky_eater, ingredient);
    }

    std::cout << "I ate " << things_eaten << " things" << std::endl;
    return 0;
    // As the program exits, we get the exception
}
C 可视化 C++

评论

0赞 Jabberwocky 11/7/2023
我可以重现这一点,但只能使用 x86,使用 x64 它运行良好。您应该使用该信息更新问题。
1赞 PCB 11/7/2023
@Jabberwocky - 我在 x86 和 x64 上都遇到了问题
0赞 Jabberwocky 11/7/2023
在此处崩溃的最简单版本(仅限 x86):godbolt.org/z/5afvYxo3c
0赞 Sam Varshavchik 11/7/2023
显示的代码没有问题。编译器错误。
1赞 Fedor 11/8/2023
过去修复的类似问题:developercommunity.visualstudio.com/t/...

答:

3赞 Fedor 11/9/2023 #1

您的代码看起来正确,可以进一步简化为

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded( Ts... )->overloaded<Ts...>;

int main() {
  int i{ 0 };
  auto lambda = overloaded{
    [&i](int) {},
    [](auto) {}
  };
}

同时在 Visual Studio 中保留堆栈损坏错误。

我向MS团队报告了它:https://developercommunity.visualstudio.com/t/Run-Time-Check-Failure-2---Stack-around/10510860

基于他们的 repospone,这是一个真正的编译器错误,该错误于 2019 年首次报告:https://developercommunity.visualstudio.com/t/Capturing-lambdas-in-function-object-res/475396