为什么我不能从lambda返回初始化列表

时间:2016-02-12 11:25:01

标签: c++ c++11 lambda c++14 initializer-list

为什么此代码无效?

  auto foo=[](){
    return {1,2};     
  };

但是,这是有效的,因为initializer list仅用于初始化vector不返回自身:

auto foo=[]()->std::vector<int>{
  return {1,2};     
};

为什么我无法退回initializer list?它可能很有用。例如,一个lambda可用于初始化vectorlist或......以及某些默认值。

3 个答案:

答案 0 :(得分:25)

Lambda返回类型推导使用auto规则,通常会推导std::initializer_list就好了。但是,语言设计者在return语句中禁止从支撑的初始化列表中进行演绎([dcl.spec.auto] / 7):

  

如果扣除是针对return语句而且初始化程序是a    braced-init-list ([dcl.init.list]),该程序格式不正确。

原因是std::initializer_list具有引用语义([dcl.init.list]/6)。 []() -> std::initializer_list<int> { return {1, 2}; }[]() -> const int & { return 1; }一样糟糕。当lambda返回时,initializer_list对象的后备数组的生命周期结束,并且留下一个悬空指针(或两个)。

Demo

#include <vector>

struct Noisy {
    Noisy()  { __builtin_printf("%s\n", __PRETTY_FUNCTION__); }
    Noisy(const Noisy&) { __builtin_printf("%s\n", __PRETTY_FUNCTION__); }
    ~Noisy() { __builtin_printf("%s\n", __PRETTY_FUNCTION__); }
};

int main()
{
    auto foo = []() -> std::initializer_list<Noisy> { return {Noisy{}, Noisy{}}; };
    std::vector<Noisy> bar{foo()};
}

输出:

Noisy::Noisy()
Noisy::Noisy()
Noisy::~Noisy()
Noisy::~Noisy()
Noisy::Noisy(const Noisy&)
Noisy::Noisy(const Noisy&)
Noisy::~Noisy()
Noisy::~Noisy()

注意在到目前为止创建的所有Noisy对象已经被销毁之后如何调用复制构造函数。

答案 1 :(得分:5)

std::initializer_list无法通过模板参数推断出来,这意味着您必须告诉lambda它是明确的:

#include <initializer_list>
#include <iostream>
#include <vector>

int main()
{
    auto foo = []() -> std::initializer_list<int> { return {1, 2}; };
    std::vector<int> bar{foo()};
    for (int x : bar) { std::cout << x << "  "; };
}

Demo。以下是initializer list proposal

背后的基本原理
  

初始化列表可以用作模板参数吗?考虑一下:

template<class T> void f(const T&);
f({ }); // error
f({1});
f({1,2,3,4,5,6});
f({1,2.0}); // error
f(X{1,2.0}); // ok: T is X
     

最后一次通话显然没有问题(假设 X {1,2.0} 本身有效)
  因为模板参数是 X 。因为我们没有引入任意的清单   类型(产品类型),我们不能将 T 推断为 {int,double} f({1,2.0}),以便电话是   一个错误。普通{}没有类型,因此 f({})也是一个错误。

     

这留下了同类列表。应该接受 f({1}) f({1,2,3,4,5,6})吗?如果是的话,
  有什么意义?如果是这样,答案必须是推导出的类型 T
  的 initializer_list 即可。除非有人提出至少一个好用的这个简单的   功能(类型 E 的同类列表推断为    initializer_list ),我们不会提出它,所有示例都会出错:没有模板
  参数可以从(不合格的)初始化列表中推导出来。谨慎的一个原因是   这是我们可以想象有人对可能的解释感到困惑   单元素列表。例如, f({1})可以调用 f&lt; int&gt;(1)吗?不,那将是
  非常不一致。

答案 2 :(得分:1)

您可以从功能中返回initializer_list,如下所示:

return std::initializer_list<int>{1, 2};

auto ret = {1, 2};
return ret;

原因是,auto变量声明使用的规则与auto返回类型扣除不同。第一个对此案例有一个特殊规则,第二个使用普通模板类型演绎。

这将在Scott Meyers Effective Modern C ++第2项中详细讨论。他还有一个关于这个主题的videoslides