C++ std::function 返回 const 引用

huangapple go评论82阅读模式
英文:

C++ std::function returning const reference

问题

我对C++还不太了解,有很多东西我还不太理解。

为什么编译器(gcc 9.4.0)会接受下面的代码呢?

std::function<const int& ()> f = []() -> int { return 1; };

我认为这会创建一个悬空引用,但为什么它可以编译通过而且没有任何警告呢?(我已经指定了-Wall -Wextra选项。)

cppreference 对于“当一个返回类型为引用的std::function从一个没有尾返回类型的lambda表达式进行初始化”这种情况提出了警告。但是我已经指定了返回类型。此外,我可以通过不使用lambda表达式来创建(我认为是)相同的行为:

int GetSomeValue() { return 1; }
std::function<const int& ()> g = GetSomeValue;

如果从返回类型中移除const会导致编译错误,这也是我期望的结果。为什么这样做会有如此重大的差异呢?

英文:

I'm new to C++, and there's a lot that I don't understand yet.

Why does the compiler (gcc 9.4.0) accept the following?

std::function&lt;const int&amp; ()&gt; f = []() -&gt; int { return 1; };

I believe this creates a dangling reference; but how/why does it compile without even a warning? (I have specified -Wall -Wextra.)

cppreference cautions about "when a std::function, whose result type is a reference, is initialized from a lambda expression without a trailing-return-type". But I did specify the return type. Furthermore, I can create (what I believe is) the same behaviour without a lambda:

int GetSomeValue() { return 1; }
std::function&lt;const int&amp; ()&gt; g = GetSomeValue;

Removing const from the return type causes a compile error, which is what I expect/want. Why does this make such a significant difference?

答案1

得分: 2

这在C++23中已经得到了修复,其中INVOKE<R>会拒绝调用满足std::reference_converts_from_temporary_v<R, decltype(INVOKE(f, t1, t2, ..., tN))>true的情况。截至目前,我所知道的唯一实现了这个检查的编译器是g++,版本为13.1及以上:

static_assert(not std::is_invocable_r_v<int const&, int()>);

这意味着g++ 13.1及以上版本将拒绝你的代码:

error: conversion from '<lambda()>' to non-scalar type 'std::function<const int&()>' requested
    2 | std::function<const int& ()> f = []() -> int { return 1; };
      |                                  ^~~~~~~~~~~~~~~~~~~~~~~~~

请注意,即使在C++23之前的模式下,g++ 13.1及以上版本也会这样做,因为GCC已经选择将此更改作为DR应用。

值得一提的是MSVC,它会发出警告C4172 "returning address of local variable or temporary"。

英文:

This is fixed in C++23, where INVOKE&lt;R&gt; rejects calls for which std::reference_converts_from_temporary_v
&lt;R, decltype(INVOKE(f, t1, t2, ..., tN))&gt;
is true. As of the present date, the only compiler I am aware of that implements this check is g++, version 13.1 and above:

static_assert(not std::is_invocable_r_v&lt;int const&amp;, int()&gt;);

This means that g++ 13.1 and above will reject your code:

error: conversion from &#39;&lt;lambda()&gt;&#39; to non-scalar type &#39;std::function&lt;const int&amp;()&gt;&#39; requested
    2 | std::function&lt;const int&amp; ()&gt; f = []() -&gt; int { return 1; };
      |                                  ^~~~~~~~~~~~~~~~~~~~~~~~~

Note that g++ 13.1 and above will do so even in pre-C++23 mode, since GCC has chosen to apply the change as a DR.

Honorable mention goes to MSVC, which warns C4172 "returning address of local variable or temporary".

答案2

得分: 1

如果你将一个返回值的函数存储在返回引用的std::function中,std::function将返回一个对临时对象的引用。

在这方面,lambda表达式并没有特殊之处,但是没有尾返回类型的lambda表达式总是返回一个值(而不是引用),所以这是一个容易犯的错误,这也是为什么要小心的原因。

关于需要const引用才能编译通过的问题,正如评论中所述,只有const左值引用可以绑定到临时对象,因此尝试返回一个非const左值引用到临时对象将无法编译通过。然而,返回一个const引用到临时对象将可以编译通过,但是不会起作用。

英文:

If you store a function that returns a value in a std::function that returns a reference, the std::function will return a reference to a temporary.

Lambdas aren't special in this regard, but a lambda without a trailing return type will always return a value (never a reference), so this is an easy mistake to make, and that's why the caution.

Regarding needing the const reference for it to compile, as comments have stated, only const lvalue references can bind to temporaries, so trying to return a non-const lvalue reference to a temporary won't compile. Returning a const reference to a temporary, however, will compile but won't work.

huangapple
  • 本文由 发表于 2023年8月9日 00:02:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/76861324.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定