首页 > 解决方案 > 为什么 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::initializer_list,则使用大括号初始化语法的调用强烈倾向于采用 的重载std::initializer_lists强烈。如果编译器有任何方法可以将使用大括号初始化程序的调用解释为采用 a 的构造函数std::initializer_list,编译器将采用该解释。

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

谁错了?为什么?

标签: c++c++11c++17initializer-listctad

解决方案


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

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

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

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


推荐阅读