为什么 ctor 中的 std::initializer_list 没有按预期运行?

#include <vector>

int main()
{
    auto v = std::vector{std::vector<int>{}};
    return v.front().empty(); // error
}

看在线演示

然而,根据 Scott Meyers 的Effective Modern C++(强调原文):

所以,我认为std::vector{std::vector<int>{}};应该产生一个对象std::vector<std::vector<int>>而不是std::vector<int>.

谁错了?为什么?

回答

Meyers 大部分是正确的(例外是T{}如果存在默认构造函数,则值初始化),但他的陈述是关于重载决议的。这发生在 CTAD 之后,CTAD 选择要使用的(以及构造函数集)。

CTAD 不“偏爱”初始化列表构造函数,因为它更喜欢复制而不是包装可嵌套模板,例如std::vectorstd::optional。(可以用推导指南覆盖它,但标准库使用默认值,正如人们所期望的那样。)这在一定程度上可以防止创建奇怪的类型,例如std::optional<std::optional<int>>,但它使泛型代码更难编写,因为它给出了

template<class T> void f(T x) {
std::vector v{x};
// …
}

一种以不规则非注入方式取决于其论点类型的含义。特别是,v可能std::vector<int>T=int或与T= std::vector<int>,尽管是std::vector<std::deque<int>>if T= std::deque<int>。不幸的是,基于其他一些类型计算一种类型的工具在通用上下文中不可用。


auto v = std::vector{std::vector<int>{}};实际上创建 astd::vector<int>因为它使用std::vector复制构造函数。它被编译器解释为:

auto vTemp = std::vector<int>{};
auto v = std::vector<int>( vTemp );

所以v最终成为 a std::vector<int>,而不是 a std::vector<std::vector<int>>

正如“P Kramer”和“Marek P”在评论中所报告的那样,以下语法将帮助任何编译器完成您的期望:

auto v = std::vector{{std::vector<int>{}}};
auto v = std::vector<std::vector<int>>{ std::vector<int>{} };

以上是为什么 ctor 中的 std::initializer_list 没有按预期运行?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>