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

时间:2017-12-12 14:50:49

标签: 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
}

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

  

警告:缩小'i'与'std :: size_t {aka long'的转换   unsigned int}'to'char'in {}

显然它试图使用vector的initializer_list。但为什么不使用更好的匹配vector<char>(size_t, char)

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

1 个答案:

答案 0 :(得分:4)

因为initializer_list构造函数(如果可能的话)优先于其他构造函数。这样可以减少边缘情况 - 特别是,您希望它使用的特定向量构造函数被认为是偶然选择的。

具体来说,标准在16.3.1.7“按列表初始化初始化”[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_tchar的缩小转换),因此在大小+值之前使用它构造函数一直被考虑。

所以回答编辑过的问题:不,你不能在没有命名其类型的情况下创建矢量。但是在C ++ 17中,您可以使用类模板参数推导,只需编写return std::vector(i, c);