使用支撑初始化列表时,为什么首选std :: initializer_list构造函数?

时间:2014-11-26 08:09:49

标签: c++ c++11 constructor language-lawyer initializer-list

考虑代码

#include <iostream>

class Foo
{
    int val_;
public:
    Foo(std::initializer_list<Foo> il)
    {
        std::cout << "initializer_list ctor" << std::endl;
    }
    /* explicit */ Foo(int val): val_(val)
    {
        std::cout << "ctor" << std::endl;
    };
};

int main(int argc, char const *argv[])
{
    // why is the initializer_list ctor invoked?
    Foo foo {10}; 
}

输出

ctor
initializer_list ctor

据我所知,值10被隐式转换为Foo(第一个ctor输出),然后初始化构造函数启动(第二个initializer_list ctor输出)。我的问题是为什么会发生这种情况?标准构造函数Foo(int)不是更好的匹配吗?即,我希望此代码段的输出只是ctor

PS:如果我将构造函数Foo(int)标记为explicit,则Foo(int)是唯一调用的构造函数,因为整数10现在无法隐式转换为{ {1}}。

3 个答案:

答案 0 :(得分:24)

§13.3.1.7[over.match.list] / p1:

  

当非聚合类类型T的对象被列表初始化时   (8.5.4),重载决策分两个阶段选择构造函数:

     
      
  • 最初,候选函数是类T的初始化列表构造函数(8.5.4),参数列表包含   初始化列表作为单个参数。
  •   
  • 如果找不到可行的初始化列表构造函数,则再次执行重载解析,其中候选函数都是   类T的构造函数和参数列表包含   初始化列表的元素。
  •   
     

如果初始化列表没有元素,T有默认值   构造函数,第一阶段被省略。在复制列表初始化中,   如果选择explicit构造函数,则初始化为   不良形成。

只要有一个可行的初始化列表构造函数,当使用列表初始化并且初始化列表至少有一个元素时,它将胜过所有非初始化列表构造函数。

答案 1 :(得分:14)

初始化列表的n2100提议非常详细地说明了使序列构造函数(他们称之为std::initializer_lists的构造函数)优先于常规构造函数的决策。有关详细讨论,请参阅附录B.结论中简明扼要地总结了这一点:

  11.4结论

     

那么,我们如何决定剩下的两个选择(“模糊”和“序列构造器”优先   超过普通的建设者)?我们的建议给出了序列构建器   优先,因为

     
      
  • 寻找所有建设者之间的含糊不清导致太多“误报”;也就是说,两者之间的冲突显然是无关紧要的   构造函数。见下面的例子。
  •   
  • 消歧本身容易出错(以及冗长)。参见§11.3中的示例。
  •   
  • 对同类列表的每个元素使用完全相同的语法很重要 - 应该进行消歧   普通的构造函数(没有常规的模式   参数)。参见§11.3中的示例。虚假的最简单的例子   positive是默认构造函数:
  •   
     

误报的最简单示例是默认构造函数:

vector<int> v; 
vector<int> v { }; // potentially ambiguous
void f(vector<int>&); 
// ...
f({ }); // potentially ambiguous
     

可以想到没有初始化的类   成员在语义上与默认初始化不同,但我们   不会使语言复杂化以提供更好的支持   案例比它们在语义上更常见的情况   相同。

     

优先使用序列构造函数将参数检查分解为   更容易理解的块,并提供更好的局部性。

void f(const vector<double>&);
// ...
struct X { X(int); /* ... */ };
void f(X);
// ...
f(1);     // call f(X); vector’s constructor is explicit
f({1});   // potentially ambiguous: X or vector?
f({1,2}); // potentially ambiguous: 1 or 2 elements of vector
     

在这里,优先考虑序列构造函数会消除   来自X的干扰。对于f(1)挑选X是该问题的变体   明确显示在§3.3中。

答案 2 :(得分:5)

整个初始化列表的东西是为了启用列表初始化,如下所示:

std::vector<int> v { 0, 1, 2 };

考虑案例

std::vector<int> v { 123 };

这会使用一个值为123的元素初始化向量,而不是123个值为零的元素。

要访问其他构造函数,请使用旧语法

Foo foo(10);