当前位置: 首页 > 知识库问答 >
问题:

为什么在使用带括号的初始值设定项列表时,首选std::initializer\u list构造函数?

缑文栋
2023-03-14

考虑代码

#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\u list ctor输出)。我的问题是为什么会这样?标准构造函数不是更好的匹配吗?一、 例如,我本以为这个片段的输出就是ctor。

PS:如果我将构造函数Foo(int)标记为显式,那么Foo(int)是唯一调用的构造函数,因为整数10现在不能隐式转换为Foo

共有3个答案

钦枫
2023-03-14

整个初始值设定项列表旨在启用列表初始化,如下所示:

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

考虑案件

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

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

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

Foo foo(10);
宇文智敏
2023-03-14

n2100关于初始值设定项列表的提案非常详细地阐述了使序列构造函数(他们称之为取std::initializer\u列表的构造函数)优先于常规构造函数的决策。详细讨论见附录B。结论简要总结如下:

11.4结论

那么,我们如何在剩下的两个选项(“歧义”和“序列构造函数优先于普通构造函数)之间做出决定?我们的建议赋予序列构造函数优先权,因为

  • 在所有构造函数中寻找歧义会导致太多的“误报”;也就是说,明显不相关的构造函数之间的冲突。请参阅下面的示例。
  • 消歧本身容易出错(以及冗长)。参见§11.3中的示例。
  • 对同构列表中的每个元素使用完全相同的语法很重要-应该对普通构造函数(没有规则的参数模式)进行消歧。参见§11.3中的示例。误报的最简单示例是默认构造函数:

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

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的干扰。选择X作为f(1)是§3.3中所示的显式问题的变体。

凌恩
2023-03-14

§13.3.1.7[超过匹配列表]/p1:

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

  • 最初,候选函数是类的初始值设定项列表构造函数(8.5.4),参数列表由初始值设定项列表作为单个参数组成

如果初始化列表没有元素并且T有默认构造函数,则省略第一阶段。在复制列表初始化中,如果选择了显式构造函数,则初始化格式错误。

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

 类似资料:
  • 我已经在网上搜索了这个问题的答案,但我还没有找到一个满意的答案。我想知道初始化结构和类类型的对象的所有规则是什么,特别是在构造函数与带括号的初始化列表方面。结构与类的规则也不同吗? 让我们假设有一个名为矩形的类或结构。 我尝试用C语言中通常使用的方式初始化矩形r,使用一个普通的带括号的初始值设定项列表。但是,g给出以下错误: 嗯……嗯。。。。乍一看,这并不是很有用的错误消息。然而,我认为它与构造函

  • 我相信现代C初始值设定项列表对于初始化对象非常有用,甚至不需要定义自己的构造函数: 但是,当我的类从另一个类继承时,这不起作用: 我尝试添加

  • 所以我正在学习构造函数初始值设定项列表,我写了以下代码: 为此我使用了g编译器。它调用的是构造函数而不是复制构造函数。它应该调用复制构造函数,因为我正在创建一个对象来创建另一个对象?这里的问题是什么,标准对此怎么说?

  • 我有2个班: A类: B类: 我创建了一个主类,它只创建了一个新的: 我得到的输出是: 如您所见,在静态初始值设定项之前调用了的构造函数。 我知道这与我创建的循环依赖有关,但我的印象是静态初始化器应该总是在构造函数之前运行。 发生这种情况的原因是什么(技术上在java实现中)? 是否建议一起避免静态初始化器?

  • 我有以下代码: 我希望输出是:“base constructor,test:50”,但事实并非如此,因为构造函数是在初始化之前调用的,没有错误或警告,它只是编译。 有什么方法可以让构造函数在之后被调用吗?或者这是总体上的糟糕设计? 我正在尝试将所有的init方法和它们的调用放入构造函数insted中,这个行为阻止了我这样做。