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

Hum*_*awi 24 c++ lambda initializer-list c++11 c++14

为什么这段代码无效?

  auto foo=[](){
    return {1,2};     
  };
Run Code Online (Sandbox Code Playgroud)

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

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

为什么我不能回来initializer list?它可能很有用.例如,一个lambda可用于初始化a vector或a list或...具有某些默认值.

T.C*_*.C. 26

Lambda返回类型推导使用auto规则,通常可以推断std::initializer_list得很好.但是,语言设计者禁止在返回语句([dcl.spec.auto]/7)中从支持的初始化列表中扣除:

如果扣除是针对一个return语句而初始化器是一个 braced -init-list([dcl.init.list]),那么程序就是格式错误.

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

演示:

#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()};
}
Run Code Online (Sandbox Code Playgroud)

输出:

Noisy::Noisy()
Noisy::Noisy()
Noisy::~Noisy()
Noisy::~Noisy()
Noisy::Noisy(const Noisy&)
Noisy::Noisy(const Noisy&)
Noisy::~Noisy()
Noisy::~Noisy()
Run Code Online (Sandbox Code Playgroud)

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

  • @HumamHelfawi您的`vector`版本工作正常,但不理想.我会考虑返回一个似乎接近你想要的`std :: array`,除非你必须手动计算对象的数量.也许你可以用`constexpr`完成一些事情来自动提取数组大小. (2认同)

Bri*_*uez 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 << "  "; };
}
Run Code Online (Sandbox Code Playgroud)

演示.以下是初始化列表提议背后的基本原理:

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

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
Run Code Online (Sandbox Code Playgroud)

显然有与上次呼叫(只要没有问题,X ^ 1,2.0}本身是有效的)
,因为模板参数是X.因为我们没有引入的任意列表
的类型(产品类型),我们不能推断Ť{INT,双}F({1,2.0}) ,从而使呼叫是
一个错误.Plain {}没有类型,因此f({})也是一个错误.

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

  • 不要将`std :: initializer_list`与支撑的init列表混淆 (3认同)
  • @HumamHelfawi是的,标准明确说`std :: initializer_list`不能被类型推导出来.这是一个[答案](http://stackoverflow.com/questions/12431495/initializer-list-and-template-type-deduction),详细介绍. (2认同)
  • @HumamHelfawi从提案中找到理由,应该让事情更清楚. (2认同)