lambda 函数 gcc 11.1 中 this = nullptr

fal*_*eel 9 c++ lambda gcc

我有以下代码:

#include <iostream>

template<typename T>
struct A
{
    T a;
    T b;
    static bool(*foo)(T, T);
};

template<>
bool(*A<int>::foo)(int, int) = [](int a, int b)->bool{ return a == b; };

struct B
{
    int b;
};

template<typename T, typename U>
T bar(U(*func)(const int&)) { return func(23).b; }

int main()
{
    A<int> a = {.a=1, .b=1};
    std::cout << std::boolalpha << A<int>::foo(a.a, a.b) << std::endl;
    std::cout << bar<int, B>([](const int& val) -> B { return {.b = val}; });
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

在 gcc 11.1 下使用-Werror=nonnull -Og -fsanitize=undefined -std=c++20标志进行编译时,每个 lambda 都会产生一个错误,如下所示:

<source>:12:71: error: 'this' pointer is null [-Werror=nonnull]
Run Code Online (Sandbox Code Playgroud)

(有关更多详细信息,请参阅https://godbolt.org/z/a4GsPW71E

据我所知,this = nullptr是代码中 UB 的生动标记,但是我的代码看起来很不起眼。为了避免该错误,您应该更改编译器的版本或对代码进行轻微更改(例如删除 lambda 的参数、模板或其他内容)。

有谁知道这个错误的原因,gcc 是否试图帮助我,或者这只是编译器中的一个错误?

Art*_*yer 8

这是我可以将您的示例简化为的最多内容:

// g++-11 -Werror=nonnull -Og -fsanitize=undefined
// (Or any level of optimization other than -O0)

bool(*bar)(int) = [](int) { return true; };
Run Code Online (Sandbox Code Playgroud)

https://godbolt.org/z/rbrczEbPx

<source>: In static member function 'static constexpr bool<lambda(int)>::_FUN(int)':
<source>:1:42: error: 'this' pointer is null [-Werror=nonnull]
    1 | bool(*bar)(int) = [](int) { return true; };
      |                                          ^
<source>:1:19: note: in a call to non-static member function '<lambda(int)>'
    1 | bool(*bar)(int) = [](int) { return true; };
      |                   ^
cc1plus: some warnings being treated as errors
Run Code Online (Sandbox Code Playgroud)

我的猜测是,-fsanitize=undefined向 lambda 添加一些代码,检查thislambda 类型中的某些内容,并且某些优化使用将lambda 中的指针operator()设置为 的内容完全删除 lambda 对象,因为它未被使用,由 捕获。thisnullptr-Werror=nonnull

但是,是的,这是一个 GCC 错误。它没有出现在 gcc-11.2 和 gcc-10 中。作为解决方法,您可以像这样替换代码:

template<>
bool(*A<int>::foo)(int, int) = [](int a, int b)->bool{ return a == b; };

// becomes

inline auto A_foo_int_fn(int a, int b)->bool{ return a == b; }
template<>
bool(*A<int>::foo)(int, int) = A_foo_int_fn;
Run Code Online (Sandbox Code Playgroud)

或者只是禁用-Wnonnull该代码区域:

#if __GNUC__ == 11 && __GNUC_MINOR__ == 1
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnonnull"
#endif

template<>
bool(*A<int>::foo)(int, int) = [](int a, int b)->bool{ return a == b; };

#if __GNUC__ == 11 && __GNUC_MINOR__ == 1
#pragma GCC diagnostic pop
#endif
Run Code Online (Sandbox Code Playgroud)

这可能与此错误有关:https://gcc.gnu.org/bugzilla/show_bug.cgi ?id=96003