初始化列表的隐式转换失败 [英] Implicit conversion failure from initializer list
问题描述
请考虑以下代码段:
#include <unordered_map>
void foo(const std::unordered_map<int,int> &) {}
int main()
{
foo({});
}
这与GCC 4.9.2失败,消息为:
This fails with GCC 4.9.2 with the message:
map2.cpp:7:19: error: converting to ‘const std::unordered_map<int, int>’ from initializer list would use explicit constructor ‘std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_map(std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::size_type, const hasher&, const key_equal&, const allocator_type&) [with _Key = int; _Tp = int; _Hash = std::hash<int>; _Pred = std::equal_to<int>; _Alloc = std::allocator<std::pair<const int, int> >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::size_type = long unsigned int; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::hasher = std::hash<int>; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_equal = std::equal_to<int>; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::allocator_type = std::allocator<std::pair<const int, int> >]’
使用其他编译器/库实现测试:
Testing with other compiler/library implementations:
- GCC< 4.9接受这个而不抱怨,
- Clang 3.5与libstdc ++失败,并显示类似的讯息
- Clang 3.5 with libc ++接受这个,
- ICC 15.something接受此(不确定使用哪个标准库)。
- GCC < 4.9 accepts this without complaining,
- Clang 3.5 with libstdc++ fails with a similar message,
- Clang 3.5 with libc++ accepts this,
- ICC 15.something accepts this (not sure which standard library it is using).
难点:
- 将
std :: unordered_map
替换为std :: map
使错误消失, - 将替换为
foo({})
code> foo({{}})也会使错误消失。
- replacing
std::unordered_map
withstd::map
makes the error go away, - replacing
foo({})
with foofoo({{}})
also makes the error go away.
,用非空的初始化器列表替换 {}
在所有情况下都能正常工作。
Also, replacing {}
with a non-empty initializer list works as expected in all cases.
是:
- 谁在这里?
- 双花括号的语法是什么?
foo({{}})
使错误消失?
- who is right here? Is the code above well-formed?
- What does the syntax with double curly braces
foo({{}})
exactly do to make the error go away?
EDIT 修复了几个错别字。
推荐答案
您的代码使用 braced-init-list的间接初始化语法称为 copy-list-initialization
The indirect initialization syntax with a braced-init-list your code is using is called copy-list-initialization.
在C ++标准的下一节中描述了为该情况选择最佳可行构造函数的重载解析过程:
The overload resolution procedure selecting the best viable constructor for that case is described in the following section of the C++ Standard:
§13.3.1.7通过列表初始化初始化
[over.match.list]
§ 13.3.1.7 Initialization by list-initialization
[over.match.list]
-
当非聚合类类型
T
的对象被列表初始化时(8.5.4) ,重载解析在两个阶段选择构造函数
:
When objects of non-aggregate class type
T
are list-initialized (8.5.4), overload resolution selects the constructor in two phases:
- 最初,候选函数是类的初始化列表构造函数
— 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.
- 如果没有可行的初始化列表构造函数,则重新执行重载分辨率,其中候选函数是 T
的所有构造函数,并且参数列表由初始化器列表的元素组成。
— 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.
如果初始化器列表没有元素,并且 T
,省略第一阶段。在复制列表初始化中,如果选择显式构造函数,初始化是不成形的。 [注意:这与其他情况(13.3.1.3,13.3.1.4)不同,其中只有转换构造函数被考虑用于复制初始化。此限制仅适用于此初始化是过载分辨率的最终结果的一部分。
If the initializer list has no elements and T
has a default constructor, the first phase is omitted. In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed. [ Note: This differs from other situations (13.3.1.3, 13.3.1.4), where only converting constructors are considered for copy-initialization. This restriction only applies if this initialization is part of the final result of overload resolution. — end note ].
em>(具有与 std :: initializer_list
According to that, an initializer-list-constructor (the one callable with a single argument matching the constructor's parameter of type std::initializer_list<T>
) is usually preferred to other constructors, but not if a default-constructor is available, and the braced-init-list used for list-initialization is empty.
这里重要的是,标准库容器的构造函数集在C ++ 11和C ++ 14之间发生了变化,原因是 LWG问题2193 。在 std :: unordered_map
的情况下,为了我们的分析,我们感兴趣的是以下区别:
What is important here, the set of constructors of the standard library's containers has changed between C++11 and C++14 due to LWG issue 2193. In case of std::unordered_map
, for the sake of our analysis, we are interested in the following difference:
explicit unordered_map(size_type n = /* impl-defined */,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type());
unordered_map(initializer_list<value_type> il,
size_type n = /* impl-defined */,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type());
C ++ 14:
unordered_map();
explicit unordered_map(size_type n,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type());
unordered_map(initializer_list<value_type> il,
size_type n = /* impl-defined */,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type());
换句话说,有一个不同的默认构造函数可以调用无参数),这取决于语言标准(C ++ 11 / C ++ 14),并且,至关重要的是,C ++ 14中的默认构造函数现在是非 - explicit
。
In other words, there is a different default constructor (the one that can be called without arguments) depending on the language standard (C++11/C++14), and, what is crucial, the default constructor in C++14 is now made non-explicit
.
引入了这种变化,可以说:
That change was introduced so that one can say:
std::unordered_map<int,int> m = {};
或:
std::unordered_map<int,int> foo()
{
return {};
}
在语义上等同于你的代码(传递 {}
作为初始化 std :: unordered_map< int,int>
的函数调用的参数。
which are both semantically equivalent to your code (passing {}
as the argument of a function call to initialize std::unordered_map<int,int>
).
也就是说,如果是符合C ++ 11的库,则错误是 expected ,因为所选 explicit
,因此代码形式不正确
That is, in case of a C++11-conforming library, the error is expected, as the selected (default) constructor is explicit
, therefore the code is ill-formed:
explicit unordered_map(size_type n = /* impl-defined */,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type());
如果是符合C ++ 14的库,错误是 / strong>,因为所选(默认)构造函数不 显式
,代码是格式正确:
In case of a C++14-conforming library, the error is not expected, as the selected (default) constructor is not explicit
, and the code is well-formed:
unordered_map();
因此,您遇到的不同行为与您使用的libstdc ++和libc ++的版本完全相关不同的编译器/编译器选项。
As such, the different behavior you encounter is solely related to the version of libstdc++ and libc++ you are using with different compilers/compiler options.
替换
std :: unordered_map
与std :: map
使错误消失。为什么?
Replacing
std::unordered_map
withstd::map
makes the error go away. Why?
我怀疑它只是因为 std :: map
用
foo({{}})
替换foo({})
也会使错误消失。为什么?
Replacing
foo({})
withfoo({{}})
also makes the error go away. Why?
因为现在这是复制列表初始化 {{}}
与非空 braced-init-list (也就是说,它有一个元素, {}
),因此第13.3.1.7节[over.match.list] / p1的第一阶段的规则(之前引用),优先于 initializer-list-constructor 到其他的。该构造函数不是显式
,因此调用是形式正确的。
Because now this is copy-list-initialization {{}}
with a non-empty braced-init-list (that is, it has one element inside, initialized with an empty braced-init-list {}
), so the rule from the first phase of § 13.3.1.7 [over.match.list]/p1 (quoted before) that prefers an initializer-list-constructor to other ones is applied. That constructor is not explicit
, hence the call is well-formed.
使用非空初始化程序列表替换
{}
在所有情况下都能正常工作。为什么?
Replacing
{}
with a non-empty initializer list works as expected in all cases. Why?
与上述相同,重载分辨率结束于第13.3.1.7节的第一阶段[ match.list] / p1。
Same as above, the overload resolution ends up with the first phase of § 13.3.1.7 [over.match.list]/p1.
这篇关于初始化列表的隐式转换失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!