Dan*_*nra 5 c++ lambda clang auto c++14
在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.
Run Code Online (Sandbox Code Playgroud)
初始化时指定所需的返回类型d可解决问题:
D d{ [&c]() -> const Int& { return c.getObj(); } }
Run Code Online (Sandbox Code Playgroud)
有趣的是,即使auto返回类型推导有意义,是不是一个错误,std::function<const Int&>用一个返回非引用的函数快乐地初始化?我也明确地写了这个:
D d{ [&c]() -> Int { return c.getObj(); } }
Run Code Online (Sandbox Code Playgroud)
编译没有问题.(上Xcode 8,clang 8.0.0)
我认为你是绊脚的地方实际上是表达c.getObj()的线return c.getObj();.
你认为表达式c.getObj()有类型const Int&.但事实并非如此; 表达式永远不会有引用类型 正如Kerrek SB在评论中指出的那样,我们有时会将表达式称为具有引用类型,作为节省冗余的快捷方式,但这会导致误解,因此我认为了解实际情况非常重要.
在声明中使用引用类型(包括作为声明中的返回类型getObj)会影响声明的事物的初始化方式,但一旦初始化,就不再有任何证据表明它最初是一个引用.
这是一个更简单的例子:
int a; int &b = a; // 1
Run Code Online (Sandbox Code Playgroud)
与
int b; int &a = b; // 2
Run Code Online (Sandbox Code Playgroud)
这两个代码完全相同(除了结果decltype(a)或decltype(b)对系统有点破解).在这两种情况下,表达式a和b两者都具有类型int和值类别"lvalue"并且表示相同的对象.它不是a"真实对象" 的情况,而是b某种伪装的指针a.他们都处于平等地位.这是一个有两个名字的对象.
现在回到你的代码:除了访问权限之外,表达式c.getObj()具有完全相同的行为c.m_obj.类型是Int,值类别是"左值".该&在的返回类型getObj()只决定了这是一个左值,它也将指定一个已经存在的(近似地说)的对象.
因此推导出的返回类型与return c.getObj();它的相同return c.m_obj;,其与模板类型推导兼容,如其他地方所述 - 不是引用类型.
NB.如果您理解这篇文章,您也会理解为什么我不喜欢被称为"自动取消引用的伪装指针"的"参考"教学法,这种错误与危险之间存在差异.
| 归档时间: |
|
| 查看次数: |
809 次 |
| 最近记录: |