为什么这个 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
选择生成警告以诊断格式错误的缩小
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
重载不可行的标准引述。
来自 13.3.2 [over.match.viable]
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 of F
.
来自 4 [conv]
An expression e
can be implicitly converted to a type T
if and
only if the declaration T t=e
; is well-formed, for some invented
temporary variable t
.
来自 8.5.1 [dcl.init.aggr]
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
,因为以下格式不正确
std::initializer_list<int> e = {1, 1.0};
{1, 1.0}
不能 隐式转换 到 std::initializer_list<int>
.
引用 13.3.2
中的引述,难道不应该暗示 A::A(std::initializer_list<int>)
在为 A a1 = {1, 1.0};
进行重载解析时不是一个可行的函数吗?找不到可行的 initializer_list
构造函数,这个语句不应该选择 A::A(int, double)
吗?
我认为您分析中的问题在于陈述
int t = 1.0;
确实格式正确 - 从 double
到 int
的隐式转换显然存在。 [over.ics.list]/4 也描述了它:
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.
初始化列表中的每个元素都可以隐式转换为int
,因此构造函数是可行的并被选中。然而,只有一旦它被选中,整个事情就会出现硬错误,[dcl.init.list]/(3.6):
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.
如您所见,要调用的构造函数是在执行缩小检查之前确定的。换句话说,初始化列表构造函数的可行性不取决于任何参数的缩小。
因此代码应该是错误的。
获得所需行为的一种方法是使用带有 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; }
Demo.
#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
选择生成警告以诊断格式错误的缩小
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
重载不可行的标准引述。
来自 13.3.2 [over.match.viable]
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]
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]
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
,因为以下格式不正确
std::initializer_list<int> e = {1, 1.0};
{1, 1.0}
不能 隐式转换 到 std::initializer_list<int>
.
引用 13.3.2
中的引述,难道不应该暗示 A::A(std::initializer_list<int>)
在为 A a1 = {1, 1.0};
进行重载解析时不是一个可行的函数吗?找不到可行的 initializer_list
构造函数,这个语句不应该选择 A::A(int, double)
吗?
我认为您分析中的问题在于陈述
int t = 1.0;
确实格式正确 - 从 double
到 int
的隐式转换显然存在。 [over.ics.list]/4 也描述了它:
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):
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.
如您所见,要调用的构造函数是在执行缩小检查之前确定的。换句话说,初始化列表构造函数的可行性不取决于任何参数的缩小。
因此代码应该是错误的。
获得所需行为的一种方法是使用带有 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; }
Demo.