为什么这个initializer_list构造函数是可行的重载? [英] Why is this initializer_list constructor a viable overload?

查看:159
本文介绍了为什么这个initializer_list构造函数是可行的重载?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

#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 of F.

来自4 [conv]

表达式e可以隐式转换为类型T,如果且 仅当声明T t=e时;格式良好,对于某些发明 临时变量t.

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]

如果 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.14,因为以下格式不正确

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;

的格式确实正确-从doubleint的隐式转换显然存在. [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 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):

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆