大括号构造函数更喜欢 initializer_list 而不是更好的匹配。为什么?

lar*_*ars 2 c++ constructor

#include <vector>

using std::size_t;

struct Foo
{
    Foo(size_t i, char c) {}
};

Foo Bar1()
{
    size_t i = 0;
    char c = 'x';
    return { i, c }; // good
}

std::vector<char> Bar2()
{
    size_t i = 0;
    char c = 'x';
    return { i, c }; // bad
}
Run Code Online (Sandbox Code Playgroud)

https://wandbox.org/permlink/87uD1ikpMkThPTaw

警告:将 'i' 从 'std::size_t {aka long unsigned int}' 的转换范围缩小到 { } 内的 'char'

显然它试图使用向量的初始化列表。但是为什么不使用更好的匹配vector<char>(size_t, char)呢?

我可以在 return 语句中使用所需的构造函数而无需再次写入类型吗?

Seb*_*edl 5

因为 initializer_list 构造函数,如果可能的话,优先于其他构造函数。这是为了使边缘情况不那么令人困惑 - 特别是,您希望它使用的这个特定向量构造函数被认为太容易被意外选择。

具体来说,标准在 16.3.1.7 "Initialization by list-initialization" [over.match.list](最新草案,N4687)中说:

(1) 当非聚合类类型 T 的对象被列表初始化使得 11.6.4 指定根据本节中的规则执行重载决议时,重载决议分两个阶段选择构造函数:

  • 最初,候选函数是类 T 的初始化列表构造函数 (11.6.4),参数列表由初始化列表作为单个参数组成。
  • 如果找不到可行的初始化列表构造函数,则再次执行重载决议,其中候选函数是类 T 的所有构造函数,参数列表由初始化列表的元素组成。

因此,如果您这样做std::vector<char>( i, c ),则此部分根本不适用,因为它不是列表初始化。应用正常的重载决议,(size_t, char)找到并使用构造函数。

但是如果你这样做了std::vector<char>{ i, c },这就是列表初始化。首先尝试初始化列表构造函数,并且(initializer_list<char>)构造函数是一个匹配项(即使它涉及从size_tto的缩小转换char),因此在考虑 size+value 构造函数之前使用它。

所以要回答编辑的问题:不,你不能在不命名向量的情况下创建它的类型。但是在 C++17 中,您可以使用类模板参数推导并简单地编写return std::vector(i, c);