为什么这个initializer_list构造函数是可行的重载? [英] Why is this initializer_list constructor a viable overload?
问题描述
#include <iostream>
#include <string>
#include <initializer_list>
class A
{
public:
A(int, bool) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
A(int, double) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
A(std::initializer_list<int>) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
int main()
{
A a1 = {1, 1.0};
return 0;
}
(此问题是对的后续操作>这个.)
上述程序无法使用clang35 -std=c++11
init.cpp:15:14: error: type 'double' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing]
A a1 = {1, 1.0};
^~~
init.cpp:15:14: note: insert an explicit cast to silence this issue
A a1 = {1, 1.0};
^~~
static_cast<int>( )
g++48 -std=c++11
选择产生警告以诊断格式不正确的缩小
while g++48 -std=c++11
chooses a produce a warning to diagnose the ill-formed narrowing
init.cpp: In function ‘int main()’:
init.cpp:15:17: warning: narrowing conversion of ‘1.0e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing]
A a1 = {1, 1.0};
^
init.cpp:15:17: warning: narrowing conversion of ‘1.0e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing]
并产生结果
A::A(std::initializer_list<int>)
我的问题是A::A(std::initializer_list<int>)
是否应该是可行的重载.以下是我认为暗示initializer_list
重载不可行的标准引号.
My question is if A::A(std::initializer_list<int>)
should be a viable overload. Below are standard quotes that I think imply that the initializer_list
overload should not be viable.
来自13.3.2 [over.match.viable]
第二,要使
F
成为可行的功能,每个功能都应存在 参数隐式转换序列,用于转换该参数 到F
的相应参数.
Second, for
F
to be a viable function, there shall exist for each argument an implicit conversion sequence that converts that argument to the corresponding parameter ofF
.
来自4 [conv]
表达式
e
可以隐式转换为类型T
,如果且 仅当声明T t=e
时;格式良好,对于某些发明 临时变量t
.
An expression
e
can be implicitly converted to a typeT
if and only if the declarationT t=e
; is well-formed, for some invented temporary variablet
.
来自8.5.1 [dcl.init.aggr]
如果 initializer-clause 是一个表达式并且变窄 转换需要转换表达式,程序是 格式不正确.
If the initializer-clause is an expression and a narrowing conversion is required to convert the expression, the program is ill-formed.
使用8.5.1
和4
,因为以下格式不正确
Using 8.5.1
and 4
, since the following is not well-formed
std::initializer_list<int> e = {1, 1.0};
{1, 1.0}
不能隐式转换为到std::initializer_list<int>
.
使用13.3.2
中的引号,这是否意味着在对A a1 = {1, 1.0};
执行重载解析时A::A(std::initializer_list<int>)
不是可行的函数?没有找到可行的initializer_list
构造函数,该语句不应该选择A::A(int, double)
吗?
Using the quote from 13.3.2
, shouldn't it imply that A::A(std::initializer_list<int>)
isn't a viable function when doing overload resolution for A a1 = {1, 1.0};
? Finding no viable initializer_list
constructors, shouldn't this statement pick A::A(int, double)
?
推荐答案
我认为您分析中的问题是以下事实:
I believe that the problem in your analysis is the fact that the statement
int t = 1.0;
的格式确实正确-从double
到int
的隐式转换显然存在. [over.ics.list]/4也对此进行了描述:
is indeed well-formed - an implicit conversion from double
to int
obviously exists. [over.ics.list]/4 also describes it:
否则,如果参数类型为
std::initializer_list<X>
,并且所有 初始化列表的元素可以隐式转换为X
,隐式转换顺序是最差的转换 必须将列表的元素转换为X
,或者如果 初始化列表没有任何元素,无法进行身份转换.
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.
初始化器列表中的每个元素都可以隐式转换为int
,因此构造函数是可行的并且可以选择.但是,只有将其选中,整个事情才会出错,[dcl.init.list]/(3.6):
Every element from the initializer list can be implicitly converted to int
, thus the constructor is viable and chosen. However, only once it is chosen, the whole thing hard-errors, [dcl.init.list]/(3.6):
列举适用的构造函数,并选择最佳的构造函数 通过过载解析(13.3,13.3.1.7). 如果收窄 要转换任何参数,都需要进行转换(参见下文), 该程序格式不正确.
The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7). If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed.
如您所见,要调用的构造函数是在执行狭窄检查之前确定的.换句话说,初始化列表构造器的可行性不取决于任何参数的缩小.
因此,代码应格式错误.
As you can see, the constructor to call is determined before the narrowing-check is performed. In other words, the viability of an initializer-list constructor is not depending on narrowing of any arguments.
Thus the code should be ill-formed.
获得所需行为的一种方法是在SFINAE中使用构造函数模板
One way to get the desired behavior is to use a constructor template with SFINAE
template <typename T, typename=std::enable_if_t<std::is_same<int, T>{}>>
A(std::initializer_list<T>) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
演示 .
这篇关于为什么这个initializer_list构造函数是可行的重载?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!