2015-01-21 34 views
11

超载分辨率低于失败clang35 -std=c++11编译:列表初始化和失败的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; 
} 

错误

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>() 

OTOH,它告诫,缩小并编译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>) 

这两种行为都有意义吗?从cppreference

All constructors that take std::initializer_list as the only argument, or as the first argument if the remaining arguments have default values, are examined, and matched by overload resolution against a single argument of type std::initializer_list

If the previous stage does not produce a match, all constructors of T participate in overload resolution against the set of arguments that consists of the elements of the braced-init-list, with the restriction that only non-narrowing conversions are allowed. If this stage produces an explicit constructor as the best match for a copy-list-initialization, compilation fails (note, in simple copy-initialization, explicit constructors are not considered at all)

引述由于收缩转换是不允许的,我希望重载解析步骤不匹配A(std::initializer_list<int>)构造,而是匹配A(int, double)之一。例如,改变A(std::initializer_list<int>)A(std::initializer_list<std::string>)clang35g++48并打印

A::A(int, double) 

如预期编译。

+0

大概你的意思是铿锵3.5。你命名这个二进制文件并不是真正有用的:)'mv clang25 clang35'“oops” – 2015-02-07 04:39:39

+0

你是对的:)这是团队在工作中维护构建系统时使用的版本控制惯例,我从来没有给过它一个想法。 – Pradhan 2015-02-07 07:00:43

回答

11

这种行为是有道理的。斯科特迈尔斯具有例如几乎完全一样本中有效的现代C++(以原强调):

If, however, one or more constructors declare a parameter of type std::initializer_list , calls using the braced initialization syntax strongly prefer the overloads taking std;:initializer_list s. Strongly. If there's any way for compilers to construe a call using a braced initializer to be a constructor taking a std::initializer_list , compilers will employ that interpretation.

实施例使用这个类:

class Widget { 
public: 
    Widget(int, bool); 
    Widget(int, double); 
    Widget(std::initializer_list<long double>); 
}; 

Widget w1(10, true); // calls first ctor 
Widget w2{10, true}; // calls std::initializer_list ctor 
Widget w3(10, 5.0); // calls second ctor 
Widget w4{10, 5.0}; // calls std::initializer_list ctor 

这两个呼叫拨打initializer_list构造函数,即使它们涉及转换两个论点 - 尽管其他构造函数都是完美匹配。

此外:

Compilers' determination to match braced initializers with constructors taking std::initializer_list s is so strong, it prevails even if the best-match std::initializer_list constructor can't be called. For example:

class Widget { 
public: 
    Widget(int, bool); // as before 
    Widget(int, double); // as before 
    Widget(std::initializer_list<bool>); // now bool 
}; 

Widget w{10, 5.0}; // error! requires narrowing conversions 

两种编译器选择正确的过载(在initializer_list之一) - 我们可以看到从标准(§13.3.1.7)要求:

When objects of non-aggregate class type T are list-initialized (8.5.4), overload resolution selects the constructor in two phases:

(1.1) — Initially, the candidate functions are the initializer-list constructors (8.5.4) of the class T and the argument list consists of the initializer list as a single argument.
(1.2) — If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the elements of the initializer list.

但调用该特定构造函数涉及缩小。在8.5.1中:

If the initializer-clause is an expression and a narrowing conversion (8.5.4) is required to convert the expression, the program is ill-formed.

所以这个程序是不合格的。在这种情况下,当gcc选择发出警告时,clang选择抛出错误。两个编译器都符合要求。

+0

哪种行为有意义? clang和gcc不同意 - clang 3.5无法编译,而g ++ 4.8.3即使在缩小转换的情况下也会选择'initializer_list'过载。上面的例子没有说明任何困难 - 转换没有缩小,因此挑选initilalizer_list过载并不奇怪。 – Pradhan 2015-01-21 02:09:18

+4

我们可以补充说这个_strong偏好_很棒,因为那样你可以'std :: vector {“hello”,“world”}'因为编译器_努力尝试并将初始化程序列表看作是std :: string类型对象的初始化程序。否则,我们会被永久卡住写入'std :: vector {std :: string {“hello”},std :: string {“world”}}'。 _想象一下当你尝试初始化某些东西时的痛苦[更简单](http://stackoverflow.com/a/28036210)_。 – sehe 2015-01-21 02:12:01

+0

@Pradhan在书中增加缩小范例 – Barry 2015-01-21 02:17:29