【问题标题】:Why does {} as function argument not lead to ambiguity?为什么 {} 作为函数参数不会导致歧义?
【发布时间】:2020-03-03 14:42:25
【问题描述】:

考虑这段代码:

#include <vector>
#include <iostream>

enum class A
{
  X, Y
};

struct Test
{
  Test(const std::vector<double>&, const std::vector<int>& = {}, A = A::X)
  { std::cout << "vector overload" << std::endl; }

  Test(const std::vector<double>&, int, A = A::X)
  { std::cout << "int overload" << std::endl; }
};

int main()
{
  std::vector<double> v;
  Test t1(v);
  Test t2(v, {}, A::X);
}

https://godbolt.org/z/Gc_w8i

打印出来:

vector overload
int overload

为什么这不会因为重载解析不明确而产生编译错误?如果第二个构造函数被移除,我们会得到两次vector overloadint{} 相比,std::vector&lt;int&gt;std::vector&lt;int&gt; 的匹配度如何?

构造函数签名肯定可以进一步修剪,但我只是被一段等效的代码欺骗了,想确保这个问题没有丢失任何重要的东西。

【问题讨论】:

  • 如果我正确地记得 {} 作为代码块,将 0 分配给变量 - 例如: const char x = {};设置为 0 (null char) ,与 int 等相同。
  • @Seti 这就是{} 在某些特殊情况下的有效作用,但通常并不正确(对于初学者来说,std::vector&lt;int&gt; x = {}; 有效,std::vector &lt;int&gt; x = 0; 无效)。可不是“{} 赋值为零”那么简单。
  • 对,它不是那么简单,但它仍然分配零 - 我认为这种行为很混乱,不应该真正使用
  • @Seti struct A { int x = 5; }; A a = {}; 在任何意义上都不分配零,它用a.x = 5 构造一个A。这与A a = { 0 }; 不同,后者确实将a.x 初始化为0。零不是{} 固有的,而是每种类型的默认构造或值初始化方式固有的。请参阅hereherehere
  • 我仍然认为默认构造的值令人困惑(需要您始终检查行为或保留很多知识)

标签: c++ language-lawyer overload-resolution


【解决方案1】:

它在 [over.ics.list],强调我的

6 否则,如果参数是非聚合类 X 并且 每个 [over.match.list] 的重载分辨率选择一个最佳值 X 的构造函数 C 执行类型对象的初始化 X 来自参数初始化器列表:

  • 如果 C 不是初始化列表构造函数,并且初始化列表有一个 cv U 类型的元素,其中 U 是 X 或派生类 从 X 中,如果 U 是,则隐式转换序列具有精确匹配等级 X,如果 U 派生自 X,则为转化排名。

  • 否则,隐式转换序列是用户定义的转换序列,第二个标准转换序列是 身份转换。

9否则,如果参数类型不是类:

  • [...]

  • 如果初始化列表没有元素,则隐式 转换序列是恒等转换。 [ 示例:

    void f(int);
    f( { } ); // OK: identity conversion
    

    结束示例 ]

std::vector 由构造函数初始化,粗体符号认为它是用户定义的转换。同时,对于int,这是身份转换,所以它胜过第一c'tor的排名。

【讨论】:

  • 是的,看起来很准确。
  • 有趣的是,标准中明确考虑了这种情况。我真的希望它是模棱两可的(看起来它可以很容易地以这种方式指定)。我无法理解你最后一句话中的推理 - 0 具有 int 类型,但没有 std::vector&lt;int&gt; 类型,这与 {} 的“无类型”性质有何“好像”?
  • @MaxLanghof - 另一种看待它的方式是,对于非类类型,它绝不是用户定义的转换。相反,它是默认值的直接初始化程序。因此在这种情况下是一个身份。
  • 那部分很清楚。我很惊讶需要将用户定义的转换为std::vector&lt;int&gt;。正如你所说,我预计“参数的类型最终决定了参数的类型”,而{}“类型”(可以这么说)std::vector&lt;int&gt; 不需要(非身份)转换为初始化一个std::vector&lt;int&gt;。标准显然是这样说的,所以就是这样,但这对我来说没有意义。 (请注意,我并不是说您或标准是错误的,只是试图将其与我的心智模型相协调。)
  • 好吧,那个编辑不是我希望的分辨率,但足够公平。 :D 感谢您的宝贵时间!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-28
  • 2018-12-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多