具有 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]
问题
- 为什么
f({1.5F});
函数调用 发现 void f(std::initializer_list<int>)
是唯一的最佳可行函数,比 void f(float)
更匹配? Afaics 这与 [over.ics.list]/4 and [over.ics.list]/9 冲突(请参阅下面的详细信息)。
我正在寻找相关标准段落的参考。
请注意,我知道有关 列表初始化 (构造函数重载)和 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)
的隐式转换序列与float
到float
的转换序列相同;即,身份转换和随后的精确匹配。但是,根据上面的示例,我的逻辑中肯定存在一些缺陷,因为调用 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):
[...]
- 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)。
(以下所有 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]
问题
- 为什么
f({1.5F});
函数调用 发现void f(std::initializer_list<int>)
是唯一的最佳可行函数,比void f(float)
更匹配? Afaics 这与 [over.ics.list]/4 and [over.ics.list]/9 冲突(请参阅下面的详细信息)。
我正在寻找相关标准段落的参考。
请注意,我知道有关 列表初始化 (构造函数重载)和 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)
的隐式转换序列与float
到float
的转换序列相同;即,身份转换和随后的精确匹配。但是,根据上面的示例,我的逻辑中肯定存在一些缺陷,因为调用 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 toX
, the implicit conversion sequence is the worst conversion necessary to convert an element of the list toX
, 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 sequenceL2
if
- (3.1.1)
L1
converts tostd::initializer_list<X>
for someX
andL2
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):
[...]
- 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)。