具有 std::initializer_list 个参数的非成员函数(/非构造函数上下文)的重载解析

Overload resolution for non-member functions (/non-constructor contexts) with std::initializer_list argument(s)

(以下所有 ISO 标准参考均参考 N4659: March 2017 post-Kona working draft/C++17 DIS,并且所有示例程序结果在 GCC 和 Clang 上对于 C++11、C++14 和 C++17 是一致的)

考虑以下示例:

#include <initializer_list>

// Denote as A.
void f(float) {}

// Denote as B.
void f(std::initializer_list<int>) {}

int main() {
    // Denote call as C1.  
    f(1.5F);    // Overload resolution picks A (trivial).

    // Denote call as C2.
    f({1.5F});  // Overload resolution picks B (and subsequently fails; narrowing).

    return 0;
}

这导致调用 C2 选择重载 B 作为最佳可行函数,随后由于列表初始化缩小而失败(由 [dcl.init.list]/7 控制):

error: type 'float' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing]

问题

我正在寻找相关标准段落的参考。

请注意,我知道有关 列表初始化 (构造函数重载)和 std::initializer_list<>(以及关于此主题的各种 SO 问题)的重载决议的特殊规则,其中强烈推荐 std::initializer_list<>,由 [over.match.list]/1. However, afaics, this does not apply here (or, if I'm wrong, at the very least arguably conflicts with the standard examples shown in [over.ics.list]/4).

管理

我自己的详细资料和调查

对于上面的 C1 和 C2 调用,按照 [over.call.func] (as per [over.match]/2.1 and [over.match.call]/1), particularly [over.call.func]/3 中的指定应用重载决策,并且两个调用的 候选函数集 是:

candidate_functions(C1) = { void f(float),
                            void f(std::initializer_list<int>) }

candidate_functions(C2) = { void f(float),
                            void f(std::initializer_list<int>) }

和每个调用中的参数列表与调用中的表达式列表相同。

根据 [over.match.viable]/2 and [over.match.viable]/3可行函数集与两个调用的候选函数相同:

viable_functions(C1) = { void f(float),
                         void f(std::initializer_list<int>) }

viable_functions(C2) = { void f(float),
                         void f(std::initializer_list<int>) }

最佳可行函数

根据 [over.match.best]/1,简而言之,对于给定的函数调用,最佳可行函数,这里特别是单参数调用(没有特殊情况viable functions of each call), 是可行的函数,对于单个参数,从单个参数到各个可行函数的单个参数。

C1:最佳可行函数

调用 f(1.5F) 具有身份标准转换(无转换),因此 A 的 完全匹配 (根据 [over.ics.scs]/3)并且 A 可以被(平凡地)明确地选为最佳可行函数。

best_viable_function(C1) = void f(float)

C2:最佳可行函数

Afaics,将调用 f({1.5F}) 的隐式转换序列排序为可行的候选者受 [over.ics.list], as per [over.ics.list]/1:

支配

When an argument is an initializer list ([dcl.init.list]), it is not an expression and special rules apply for converting it to a parameter type.

将 C2 匹配到 A

对于单个参数为基本 float 类型的 A 的匹配,[over.ics.list]/9 and particularly [over.ics.list]/9.1 应用 [emphasis 我的]:

Otherwise, if the parameter type is not a class:

  • (9.1) if the initializer list has one element that is not itself an initializer list, the implicit conversion sequence is the one required to convert the element to the parameter type; [ Example:

    void f(int);
    f( {'a'} );             // OK: same conversion as char to int
    f( {1.0} );             // error: narrowing
    

    — end example ]

  • [...]

这可以说意味着匹配调用f({1.5F}}f(float)的隐式转换序列与floatfloat的转换序列相同;即,身份转换和随后的精确匹配。但是,根据上面的示例,我的逻辑中肯定存在一些缺陷,因为调用 C2 甚至不会导致模棱两可的最佳可行函数。

将 C2 匹配到 B

用于匹配单个参数 [over.ics.list]/4 的 B [emphasis mine]:

Otherwise, if the parameter type is std​::​initializer_­list<X> and all the elements of the initializer list can be implicitly converted to X, the implicit conversion sequence is the worst conversion necessary to convert an element of the list to X, or if the initializer list has no elements, the identity conversion. This conversion can be a user-defined conversion even in the context of a call to an initializer-list constructor. [ Example:

void f(std::initializer_list<int>);
f( {} );                // OK: f(initializer_­list<int>) identity conversion
f( {1,2,3} );           // OK: f(initializer_­list<int>) identity conversion
f( {'a','b'} );         // OK: f(initializer_­list<int>) integral promotion
f( {1.0} );             // error: narrowing

[...]

— end example ]

在此示例中,隐式转换序列因此是 将列表的单个 float 元素转换为 int 所需的最差转换,这是 conversion 排名标准转换序列 ([over.ics.scs]/3),特别是根据 [conv.fpint]/1 的缩小转换.

最佳可行函数

根据我自己对上述标准段落的解释,最佳可行函数应该与 C1 调用相同,

best_viable_function(C2) = void f(float) ?

但我显然遗漏了一些东西。

列表初始化序列:一个序列转换为std::initializer_list

时的特殊情况排序

[over.ics.rank]/3.1 applies for this case, and takes precedence over the other rules of [over.ics.rank]/3[强调我的]:

List-initialization sequence L1 is a better conversion sequence than list-initialization sequence L2 if

  • (3.1.1) L1 converts to std​::​initializer_­list<X> for some X and L2 does not, or, if not that
  • (3.1.2) [...]

even if one of the other rules in this paragraph would otherwise apply. [ Example:

void f1(int);                                 // #1
void f1(std::initializer_list<long>);         // #2
void g1() { f1({42}); }                       // chooses #2

void f2(std::pair<const char*, const char*>); // #3
void f2(std::initializer_list<std::string>);  // #4
void g2() { f2({"foo","bar"}); }              // chooses #4

 — end example ]

意味着在比较 "C2 calls A" 中的最佳匹配时,[over.ics.rank]/3.2 and [over.ics.rank]/3.3, covering distinguishing implicit conversion sequences by means of standard conversion sequences and user defined conversion sequences, respectively, does not apply, which in turn means [over.ics.list]/4 and [over.ics.list]/9 将不会用于对隐式转换序列进行排名"C2 calls B".


这个主题是 C++11 和 C++14 中的一个缺陷

毫不奇怪,这些转换可能会违反直觉,而且管理它们的规则很复杂。在最初的 C++11 和 C++14 ISO 标准版本中,调用 f({1.5F}); 实际上有模糊的排名规则 w.r.t。最好的可行函数,在 CWG Defect Report 1589 [emphasis mine]:

1589. Ambiguous ranking of list-initialization sequences

Section: 16.3.3.2 [over.ics.rank]
Status: CD4
Submitter: Johannes Schaub
Date: 2012-11-21

[Moved to DR at the November, 2014 meeting.]

The interpretation of the following example is unclear in the current wording:

void f(long);
void f(initializer_list<int>);
int main() { f({1L});

The problem is that a list-initialization sequence can also be a standard conversion sequence, depending on the types of the elements and the type of the parameter, so more than one bullet in the list in 16.3.3.2 [over.ics.rank] paragraph 3 applies:

[...]

这些项目符号给出了与上面的例子相反的结果,并且有 是选择的实现方差。

[...]

提议的决议(2014 年 6 月):

此问题已通过问题解决方案得到解决 1467.

CWG Defect Report 1467 最终也解决了 DR 1589,特别是添加了上面 [over.ics.rank]/3 引用的相关部分 [emphasis 我的]:

1467. List-initialization of aggregate from same-type object

[...]

Proposed resolution (June, 2014):

[...]

  1. Move the final bullet of 16.3.3.2 [over.ics.rank] paragraph 3 to the beginning of the list and change it as follows:

[...]

even if one of the other rules in this paragraph would otherwise apply. [Example: ... — end example]

This resolution also resolves issues 1490, 1589, 1631, 1756, and 1758.

GCC 和 Clang 等编译器已将 DR 1467 向后移植到早期标准(C++11 和 C++14)。