在 C++14 中,为什么具有推导返回类型的 lambda 函数默认会删除返回类型中的引用? IIUC,因为具有推导返回类型(没有显式尾随返回类型)的 C++14 lambda 函数的返回类型为auto
,它会删除引用(除其他外)。
为什么做出这个决定?在我看来,当返回语句返回的内容是删除引用时,这似乎是一个陷阱。
这种行为给我带来了以下令人讨厌的错误:
class Int {
public:
Int(int i) : m_int{i} {}
int m_int;
};
class C {
public:
C(Int obj) : m_obj{obj} {}
const auto& getObj() { return m_obj; }
Int m_obj;
};
class D {
public:
D(std::function<const Int&()> f) : m_f{f} {}
std::function<const Int&()> m_f;
};
Int myint{5};
C c{myint};
D d{ [&c](){ return c.getObj(); } } // The deduced return type of the lambda is Int (with no reference)
const Int& myref = d.m_f(); // Instead of referencing myint, myref is a dangling reference; d.m_f() returned a copy of myint, which is subsequently destroyed.
初始化时指定所需的返回类型d
解决问题:
D d{ [&c]() -> const Int& { return c.getObj(); } }
有趣的是,即使auto
返回类型推导是有道理的,这不是一个错误吗std::function<const Int&>
使用返回非引用的函数愉快地初始化?我也通过明确地写道:
D d{ [&c]() -> Int { return c.getObj(); } }
编译没有问题。 (在Xcode 8
, clang 8.0.0
)
我认为你绊倒的地方实际上是这个表达c.getObj()
在行中return c.getObj();
.
你认为表达c.getObj()
有类型const Int&
。然而事实并非如此;表达式从来没有引用类型。正如 Kerrek SB 在评论中指出的那样,我们有时会谈论表达式,就好像它们具有引用类型一样,作为节省冗长的捷径,但这会导致误解,所以我认为了解真正发生的事情很重要。
在声明中使用引用类型(包括作为返回类型,如getObj
的声明)会影响被声明的事物的初始化方式,但是一旦初始化,就不再有任何证据表明它最初是一个引用。
这是一个更简单的例子:
int a; int &b = a; // 1
versus
int b; int &a = b; // 2
These two codes are exactly identical (except for the result of decltype(a)
or decltype(b)
which is a bit of a hack to the system). In both cases the expressions a
and b
both have type int
and value category "lvalue" and denote the same object. It's not the case that a
is the "real object" and b
is some sort of disguised pointer to a
. They are both on equal footing. It's one object with two names.
现在回到你的代码:表达式c.getObj()
具有与完全相同的行为c.m_obj
,除了访问权限。类型是Int
值类别是“左值”。这&
在返回类型中getObj()
仅表明这是一个左值,并且它还将指定一个已经存在的对象(大约而言)。
所以推导出来的返回类型return c.getObj();
是一样的return c.m_obj;
,为了与模板类型推导兼容,如其他地方所述,它不是引用类型。
注意。如果你理解这篇文章,你也会明白为什么我不喜欢“引用”的教学法被教导为“自动解引用的伪装指针”,这是错误和危险之间的。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)