为什么这个initializer_list构造函数是一个可行的重载?

时间:2015-01-21 18:16:39

标签: c++ c++11 language-lawyer overload-resolution list-initialization

#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;
}

(这个问题是对this的跟进。)

上述程序无法使用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]

  

其次,要使F成为可行的函数,每个都应该存在   参数一个转换该参数的隐式转换序列   到F的相应参数。

来自4 [conv]

  

如果和,表达式e可以隐式转换到类型T   只有声明T t=e;对于一些发明的人来说,它是完美的   临时变量t

来自8.5.1 [dcl.init.aggr]

  

如果 initializer-clause 是表达式和​​缩小范围   转换表达式需要转换,程序是   不良形成。

使用8.5.14,因为以下内容格式不正确

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)吗?

1 个答案:

答案 0 :(得分:4)

我认为你的分析中的问题是声明

int t = 1.0;

确实是格式良好的 - 显然存在从doubleint的隐式转换。 [over.ics.list] / 4也描述了它:

  

否则,如果参数类型为std::initializer_list<X>且全部   初始化列表的元素可以隐式转换为   X,隐式转换序列是最差的转换   必须将列表的元素转换为X,或者如果是。{   初始化列表没有元素,标识转换。

初始化列表中的每个元素都可以隐式转换为int,因此构造函数是可行的并且是可选的。但是,只有选择一次,整个事情才会出现错误,[dcl.init.list] /(3.6):

  

枚举适用的构造函数,并选择最佳构造函数   通过重载决议(13.3,13.3.1.7)。 如果缩小   转换(见下文)是转换任何参数所必需的,   该计划格式不正确。

如您所见,在执行窄化检查之前确定要调用的构造函数。换句话说,初始化列表构造函数的可行性不依赖于任何参数的缩小。
因此代码应该是格式错误的。

获得所需行为的一种方法是使用带有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