You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

VS2019中EBO多继承实现overload时栈损坏问题求助

Fixing "Run-Time Check Failure #2 - Stack around the variable 'function' was corrupted" for overload template in Visual Studio 2019

This is a known quirk with Visual Studio's implementation of the Empty Base Optimization (EBO) when mixing stateless and stateful lambdas in multiple inheritance setups. The compiler's object layout calculations get confused in these scenarios—especially when functor order changes or when combining captured and non-captured lambdas—leading to stack corruption.

Here are several reliable fixes to make your overload template work stably in VS2019:


1. Use std::variant + std::visit instead of multiple inheritance

This approach completely avoids EBO-related layout issues by wrapping your callables in a variant instead of inheriting from them:

#include <variant>
#include <type_traits>

template <typename... Callables>
struct overload_variant {
    std::variant<Callables...> funcs;

    template <typename... Args>
    decltype(auto) operator()(Args&&... args) const {
        return std::visit([&](const auto& func) {
            return func(std::forward<Args>(args)...);
        }, funcs);
    }
};

template <typename... Callables>
overload_variant(Callables...)->overload_variant<Callables...>;

Use it exactly like your original overload struct:

TEST(overload_test, test) {
    int i = 0;
    const overload_variant function {
        [](int) { return Type::Int; },
        [i](double) { auto a = i; return Type::Double; },
    };
    EXPECT_EQ(function(0.0), Type::Double);
    EXPECT_EQ(function(0), Type::Int);
}

Since we’re not relying on multiple inheritance here, VS’s EBO bugs can’t affect the object layout.


2. Force a non-empty base class to disable problematic EBO

If you want to stick with the inheritance approach, adding a trivial non-empty base class prevents the compiler from applying EBO in a buggy way:

struct NonEmptyBase { char dummy; };

template <typename... Callables>
struct overload : NonEmptyBase, public Callables... {
    using Callables::operator()...;
};

template <typename... Callables>
overload(Callables...)->overload<Callables...>;

The dummy char ensures the base class has a non-zero size, so the compiler won’t try to optimize away base class offsets incorrectly. This makes the object layout predictable and eliminates stack corruption.


3. Wrap lambdas in a uniform wrapper

Another fix is to wrap every lambda (stateless and stateful) in a small struct to ensure consistent base class layout. This removes the inconsistency between stateless lambdas (often treated as empty types) and stateful ones:

template <typename Func>
struct LambdaWrapper {
    Func func;
    template <typename... Args>
    decltype(auto) operator()(Args&&... args) const {
        return func(std::forward<Args>(args)...);
    }
};

template <typename Func>
LambdaWrapper(Func)->LambdaWrapper<Func>;

template <typename... Callables>
struct overload : public LambdaWrapper<Callables>... {
    using LambdaWrapper<Callables>::operator()...;
};

template <typename... Callables>
overload(LambdaWrapper<Callables>...)->overload<Callables...>;

Use it by wrapping each lambda:

const overload function {
    LambdaWrapper{[](int) { return Type::Int; }},
    LambdaWrapper{[i](double) { auto a = i; return Type::Double; }},
};

This ensures every base class has a consistent size (even stateless lambdas are wrapped in a struct with a member), so VS’s layout calculations don’t break.


4. Upgrade to use C++20's std::overload (if possible)

Visual Studio 2019 supports partial C20 features, and starting from version 16.10, std::overload is available in the standard library. If you can adjust your project settings to use C20, replacing your custom overload with the standard one will resolve the issue—it’s implemented to avoid such compiler-specific bugs.

If you can’t upgrade fully, you can use this standard-compliant implementation (which is tested to work with VS’s quirks):

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

If you still see issues, combine this with the NonEmptyBase approach above for extra stability.


Why your original __declspec(empty_bases) fix was inconsistent

The __declspec(empty_bases) hint tells the compiler to apply EBO, but VS’s implementation has bugs when mixing empty (stateless lambda) and non-empty (stateful lambda) bases. Depending on the order and combination of lambdas, the compiler miscalculates the object’s size and stack alignment, leading to occasional corruption. The fixes above either avoid EBO entirely or make the layout consistent so the compiler can’t mess up the offsets.

内容的提问来源于stack exchange,提问作者anton.vodostoev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 09:17:08