this = lambda函数gcc 11.1中的空指针

rbl8hiat  于 2022-11-13  发布在  其他
关注(0)|答案(2)|浏览(158)

下面的代码是:

#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;
}

在gcc 11.1下使用-Werror=nonnull -Og -fsanitize=undefined -std=c++20标志编译时,它会根据lambda生成如下错误:

<source>:12:71: error: 'this' pointer is null [-Werror=nonnull]

(for更多详细信息,请参见https://godbolt.org/z/a4GsPW71E
正如我所知,this = nullptr在代码中是UB的一个生动的标记,但是我的代码看起来很不显眼。为了避免这个错误,你应该改变编译器的版本或者在代码中做一个微小的改变(例如,删除lambda的参数,或者模板,或者其他东西)。
有没有人知道这个错误的原因,gcc试图帮助我,或者它只是一个错误的编译器?

qlvxas9a

qlvxas9a1#

下面是我能把你的例子简化到的最大限度:

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

bool(*bar)(int) = [](int) { return true; };

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

我的猜测是-fsanitize=undefined向lambda添加了一些代码,检查lambda类型的operator()中的this,并且一些优化通过将lambda中的this指针设置为nullptr来完全删除lambda对象,因为它未被使用,这被-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;

或者只禁用该代码区域周围的-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

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

ztmd8pv5

ztmd8pv52#

我将称之为编译器错误。错误信息显然是无意义的:
:在静态成员函数'static constexpr bool A::::_FUN(int,int)'中::12:71:错误:“this”指针为空[-Werror=非空]
“静态成员函数”...“”this“指针为空”
我可以理解这里发生了什么。一个非捕获的lambda可以被转换成一个函数指针。lambda是一个对象,所以GCC内部有一个this指针是合理的,但是当operator(int,int),也就是_FUN被转换成一个自由函数时,这个指针就没有意义了。

相关问题