【问题标题】:Using "auto" to deduce the type of a nested initializer list使用“auto”推断嵌套初始化列表的类型
【发布时间】:2019-12-24 16:33:11
【问题描述】:

我正在尝试使用auto 自动推断嵌套std::initializer_list 的类型。

auto list = {
    {{ 0, 1}, { 2, 3 }},
    {{ 4, 5}, { 6, 7 }},
};

这里的实际类型是std::initializer_list<std::initializer_list<std::initializer_list<int>>>,但是当我尝试编译它时,我收到一个错误,指出auto 无法推断出类型。有没有办法让auto 识别这样的构造?

我有一个程序,其中这些初始化列表可以是任意大小和深度,因此硬编码类型是不切实际的。

附加信息:

我在这里找到了关于初始化列表的文档:https://en.cppreference.com/w/cpp/language/list_initialization

braced-init-list 不是表达式,因此没有类型,例如decltype({1,2}) 格式不正确。没有类型意味着模板类型推导不能推导出与花括号初始化列表匹配的类型,因此给定声明 template<class T> void f(T); 表达式 f({1,2,3}) 是格式错误的。但是,模板形参也可以推导出来,就像std::vector<int> v(std::istream_iterator<int>(std::cin), {}) 的情况一样,其中迭代器类型由第一个参数推导,但也用于第二个参数位置。使用关键字 auto 进行类型推导有一个特殊例外,它在复制列表初始化中将任何花括号初始化列表推导出为 std::initializer_list

文档似乎表明使用auto 进行类型推导有一个特殊例外,因此您会认为这会起作用...但似乎当您使用嵌套列表时auto 无法推断类型.

【问题讨论】:

  • 如果你知道类型,就声明它。使用auto 似乎很懒惰,就像在允许var 关键字的语言中使用的糟糕的编程策略一样。 C++ 是一种类型化语言,这意味着每个变量都有一个一旦声明就不能改变的类型。除非您的代码文件可以包含多少个字符,否则您只需声明类型并完成它。
  • @JoelTrauger 阅读了问题的最后一句话。大小/深度可以变化。无法在任何地方硬编码类型,因为类型并不总是相同。
  • 我不完全确定,但我认为即使您手动指定类型,您也会得到一个“悬空”初始化列表。见stackoverflow.com/questions/59136795/…en.cppreference.com/w/cpp/utility/initializer_list
  • @JoelTrauger 我不是在寻找运行时灵活性,这是编译时灵活性。 auto 在编译时推断类型,而不是运行时。
  • @JoelTrauger 这既不是“穷”也不是“懒惰的编程策略”。它按预期使用强类型——作为程序员的福音,而不是语法障碍。查看具有高级类型系统的语言,例如 Haskell 和 Ocaml,了解它们如何处理这个问题。

标签: c++ initializer-list auto type-deduction


【解决方案1】:

我有一个程序,其中这些初始化列表可以是任意大小和深度,因此硬编码类型是不切实际的。

那么你需要解决这个问题。

您不应该将花括号初始化列表视为一种快速而肮脏的方式来创建值数组,而无需考虑它们的类型。那不是他们的目的。它们的目的是初始化值。 std::initializer_list 类型旨在作为初始化某些类型过程中的中间阶段(这就是为什么采用单个 initializer_list 的构造函数在列表初始化中具有特殊含义。

如果您想要拥有各种深度的数组的数组等,那么您将需要弄清楚该构造需要是哪种类型并输入它。 auto 只能推导出一级braced-init-list;如果需要更深层次,则需要明确指定类型。

使用auto进行类型推导有一个特殊例外

是的,有。但它只适用于推导auto 本身 的列表,不适用于auto 的推导需要的任何东西。

为了使auto list = {{1, 2, 3}}; 工作,编译器必须推导出两种类型:用于{1, 2, 3} 的类型和用于list 的类型。推导list 的类型需要推导嵌套的braced-init-list 的类型。但是您不能推断出花括号初始化列表的类型。因此它不起作用。

还应该注意的是,即使它确实有效,它实际上也不会有效。原因是list 内部的initializer_list 将引用一个临时数组。将在初始化表达式结束时销毁的临时数组。这与string_view sv = std::string("foo"); 没有产生有用的东西的原因基本相同。

【讨论】:

  • 哪里有文件证明auto 只能推导出单级花括号初始化列表?我知道这可能是真的,但我在任何地方的文档中都找不到明确说明。
  • @tjwrona1992:您在这里寻找什么样的文档?
  • 标准中的任何官方信息或像 cppreference 这样的可信网站都说明了如果您尝试使用 auto 推断嵌套初始化器列表的类型会发生什么。它说对于带有初始化列表的auto 有特殊的类型推导规则,所以它应该可以工作,但它没有说明为什么它不适用于嵌套列表。
  • @tjwrona1992:“从字面上看,标准中的任何官方内容都说明了如果您尝试使用 auto 推断嵌套初始化器列表的类型会发生什么。”标准作品。对于专门用于推断auto 变量的嵌套大括号初始化列表没有特殊规则。用于auto 变量的括号初始化列表有一个特殊规则,但是括号初始化列表 within 并没有神奇地得到特殊处理,因为外部list 恰好与 auto 变量一起使用。
  • 好吧,我想我明白你的意思了...auto 的特殊规则不会超过一层。我想这是有道理的。
猜你喜欢
  • 2021-05-31
  • 1970-01-01
  • 1970-01-01
  • 2018-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-04
  • 2016-04-12
相关资源
最近更新 更多