【问题标题】:C++0x: Overload ResolutionC++0x:重载解析
【发布时间】:2011-07-19 08:51:39
【问题描述】:

在Standard-Text中有一个例子在8.5.4 (3) List-initialization [dcl.init.list]

struct S {
    S(std::initializer_list<double>);  // #1
    S(const std::string&);             // #2
};
const S& r1 = { 1, 2, 3.0 };  // OK: invoke #1
const S& r2 { "Spinach" };    // OK: invoke #2 !!!

(示例是关于 ref-to-temp,但我在这里指的是重载解析)。

而 Scott Meyers 在他的演讲/幻灯片中讲述了一个不同的故事:

std::initializer_list 参数总是优先于其他类型:

class Widget {
public:
    Widget(double value, double uncertainty);           // #1
    Widget(std::initializer_list<std::string> values);  // #2
};
double d1, d2;
Widget w1 { d1, d2 }; // tries to call #2; fails because
                      // no double ⇒ string conversion

这些例子略有不同,但它们不是同一件事吗?何时以及如何使用initializer_list-constructors 解决重载问题?还是这里有不同的问题?

这两种情况下的重载是如何决定的?如果两者都是正确的,我在这里错过了什么?

编辑/澄清 Keric 的评论:我的感觉是,这两个例子相互矛盾:

  • Std 给出了一个示例,其中将 const char* 转换为 string,这与提供的 initializer_list&lt;int&gt; 不匹配,因此使用提供的“正常”const string&amp;-c'tor。
  • Scott 的示例在intializer_list&lt;int&gt;-c'tor 可用并因此选择时使用{double, double} 进行初始化,因为他认为该列表是首选。因此,以这种方式初始化时,永远不会选择还提供的(double, double)-c'tor。

当然,Std 总是正确的,但也许我错误地应用了这个例子。 Std 示例中有&amp;,我认为这与我的问题无关,但也许我错了。

Scott 的幻灯片是最新的,我看不到标准中的相关部分已经改变到这方面(尽管很难将所有内容都纳入范围,因为它有点“广泛传播”:-)

Edit-2:我收到了 Scott 本人发来的邮件,说有一个 late change in the Standard 没有被合并到幻灯片中。

【问题讨论】:

  • 如果标准是最新的标准,那么标准是正确的。某人(无论多么有影响力)在幻灯片上放什么并不重要。如果标准实际上在语言本身中说(示例是非规范性的),那么它应该是这样工作的。
  • 我不完全确定您的问题是什么,或者您发现示例的哪一部分不清楚或矛盾。你能澄清一下吗?旧的 N3242 说 (8.5.4(2)):“在列表初始化中,初始化列表构造函数比其他构造函数更受青睐”。我认为这很清楚,不是吗? (另见 13.3.3.1.5 转换为初始化列表。)
  • 可以将const char[] 转换为std::initializer_list&lt;double&gt;(真的是doubles 的数组)吗?我认为问题在于你的构造函数参数仍然需要匹配,不管有没有隐式转换。
  • 令人惊讶的是,gcc 4.5.1。第二次初始化失败:ideone.com/Szbts

标签: c++11 overloading initializer-list


【解决方案1】:

根据 2011 年 4 月 5 日的工作草案 N3291,对初始化列表进行了一些更改,因此 Scott Meyer 的幻灯片可能来自旧数据。

根据第 13.3.1.7 节(如此接近 1337):

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

所以它确实更喜欢初始化器列表。但是如果初始化列表不匹配,那么它将检查常规构造函数以查看它们是否匹配。

接着说,如果初始化列表为空,则使用默认构造函数。

【讨论】:

  • 好吧,你会说斯科特的例子不正确了吗?因为“它会检查常规构造函数” 那么呢?
  • 这太令人惊讶了,我原以为非初始化器列表重载是更多特定的。再说一次,如果 Foo({1,2,3}) 使用初始化列表,如果 Foo({1,2}) 没有...
  • @spraff:不完全是。如果你把{...} 放在(...) 里面,你就可以明确init-list。有了这个你可以控制,两者都将被放入 init-list (我认为)。但是如果你省略了外部的(...),你会得到Foo{1,2,3}Foo{1,2}。在这两种情况下,您都让编译器决定,然后 init-list 是首选。效果一样。
  • 将它们放在(...) 中,就像在 scott 的示例类中的({d1, d2}) 中一样,仍将使用非初始化列表构造函数(因为 init-list ctor 不匹配)。但是,不是直接而是使用复制构造函数来创建一个临时的Widget
  • @Nicol Bolas:虽然我已经有了open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1151 的更正版本,但我仍然对 Scotts 的例子感到困惑。感谢您指出我正确的段落。
猜你喜欢
  • 1970-01-01
  • 2011-07-28
  • 1970-01-01
  • 1970-01-01
  • 2022-12-06
  • 2010-10-08
  • 2011-07-25
  • 2011-09-17
  • 1970-01-01
相关资源
最近更新 更多