(以下所有ISO标准参考均参考N4659:2017年3月Kona工作草案/ 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和[over.ics.list] / 9冲突(请参阅下面的详细信息)。我正在寻找有关相关标准段落的参考。
请注意,我知道有关列表初始化(构造函数重载)和std::initializer_list<>
(以及有关此主题的各种SO问题)的重载解析的特殊规则,在[over.match.list] / 1的std::initializer_list<>
控制下,强烈推荐使用这些规则。但是,afaics在这里不适用(或者,如果我错了,至少可以说与[over.ics.list] / 4中显示的标准示例发生冲突)。
对于上述C1和C2调用,都按[over.call.func](根据[over.match] /2.1和[over.match.call] / 1)中的指定应用重载解析,尤其是[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和[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,给定函数调用的最佳可行函数是可行的,这里特别是单参数调用(每个调用的可行函数没有特殊情况)对于单个参数,具有从相应可行函数的单个参数到单个参数的最佳隐式转换序列的函数。
该调用f(1.5F)
具有身份标准转换(不转换),因此可以(平凡且)毫无疑问地选择A与A的完全匹配(按照[over.ics.scs] / 3)作为最佳可行函数。
best_viable_function(C1) = void f(float)
Afaics按照[over.ics.list] / 1的规定,将呼叫的隐式转换序列排名f({1.5F})
为可行候选者,由[over.ics.list]控制:
当参数是初始化程序列表([dcl.init.list])时,它不是表达式,并且特殊规则适用于将其转换为参数类型。
将C2匹配到A
对于单个参数为基本float
类型的A的匹配,[over.ics.list] / 9尤其是[over.ics.list] /9.1适用[强调我的]:
否则,如果参数类型不是类:
(9.1)如果初始化列表中有一个本身不是初始化列表的元素,则隐式转换序列是将元素转换为参数类型所需的序列;[ 示例:
void f(int); f( {'a'} ); // OK: same conversion as char to int f( {1.0} ); // error: narrowing
— 结束示例 ]
[...]
这可以说是意味着对于呼叫匹配隐式转换序列f({1.5F}}
以f(float)
作为相同的转换序列float
到float
; 即身份转换和随后的完全匹配。但是,按照上面的示例,由于调用C2甚至不会导致模棱两可的最佳可行函数,因此我的逻辑肯定存在一些缺陷。
使C2与B匹配
对于与单个参数[over.ics.list] / 4适用的B匹配[重点]:
否则,如果参数类型为,
std::initializer_list<X>
并且初始化列表的所有元素都可以隐式转换为X
,则隐式转换序列是将列表的元素转换为所需的最差转换X
,或者如果初始化列表没有元素,则标识转换。即使在调用initializer-list构造函数的上下文中,此转换也可以是用户定义的转换。[示例: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 [...]
—结束示例]
因此,在此示例中,隐式转换序列是将列表的单个元素转换为的最差转换,这是转换排序的标准转换序列([over.ics.scs] / 3),尤其是根据[conv.fpint] / 1。float
int
最佳可行功能
根据我自己对上述标准段落的解释,最佳可行功能应与C1调用相同,
best_viable_function(C2) = void f(float) ?
但我显然缺少了一些东西。
std::initializer_list
[over.ics.rank] /3.1适用于这种情况,并且优先于[over.ics.rank] / 3 [强调我的]的其他规则:
列表初始化序列
L1
是一个更好的转换序列比列表初始化序列L2
,如果
- (3.1.1)
L1
转换为std::initializer_list<X>
forX
,L2
但不转换为,如果不是,则转换为- (3.1.2)[...]
即使本条款的其他规定之一适用。[示例:
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
— 结束示例 ]
表示[over.ics.rank] /3.2和[over.ics.rank] /3.3分别涉及通过标准转换序列和用户定义的转换序列来区分隐式转换序列的情况并不适用,这反过来意味着[在比较“ C2调用A”与“ C2调用B”中的最佳匹配时,将不会使用over.ics.list] / 4和[over.ics.list] / 9对隐式转换序列进行排名。
这些转换可能会违反直觉,并且控制它们的规则很复杂也就不足为奇了。在原来的C ++ 11和C ++ 14个的ISO标准的版本中,调用f({1.5F});
实际上有暧昧的排名规则WRT的最佳可行函数,它浑身上下CWG缺陷报告1589 [重点煤矿]:
1589.列表初始化序列的模糊排序
栏目:16.3.3.2 [over.ics.rank]
状态:CD4
提交者:Johannes Schaub
日期:2012-11-21[在2014年11月的会议上移至DR。]
当前的措词尚不清楚以下示例的解释:
void f(long); void f(initializer_list<int>); int main() { f({1L});
问题是列表初始化序列也可以是标准转换序列,具体取决于元素的类型和参数的类型,因此16.3.3.2中的列表中有多个项目符号[over.ics.rank]第3款适用:
[...]
对于上面的示例,这些项目符号给出相反的结果,并且在其中选择了实现差异。
[...]
拟议决议(2014年6月):
通过问题1467的解决方案可以解决此问题。
CWG缺陷报告1467最终还解决了DR 1589,特别是在上面的[over.ics.rank] / 3 [强调我的]中引用了相关部分:
1467.来自同一类型对象的聚合的列表初始化
[...]
拟议决议(2014年6月):
[...]
- 将16.3.3.2 [over.ics.rank]第3段的最后一个项目符号移到列表的开头,并进行如下更改:
[...]
即使本段落中的其他规则之一适用。[示例:...-结束示例]
此决议还解决了问题1490、1589、1631、1756和1758。
此后,诸如GCC和Clang之类的编译器已将DR 1467向后移植到较早的标准(C ++ 11和C ++ 14)。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句