c++11数组初始化不会调用复制构造函数
Posted
技术标签:
【中文标题】c++11数组初始化不会调用复制构造函数【英文标题】:c++11 array initialization won't call copy constructor 【发布时间】:2015-04-03 22:38:23 【问题描述】:我正在制作一个小类,它使用一个以其大小为模板的数组。这是一些代码...
.hpp
template <size_t N>
class KeyCombinationListener
public:
KeyCombinationListener(
const std::array<sf::Keyboard::Key, N>& sequence,
std::function<void (void)> fn
);
private:
std::array<sf::Keyboard::Key, N> combo;
std::function<void (void)> callback;
;
.cc
template <size_t N>
KeyCombinationListener<N>::KeyCombinationListener(
const array<sf::Keyboard::Key, N>& sequence, function<void (void)> fn
) : combo(sequence), progressbegin(combo), callbackfn
在构造函数的成员初始化中,我不能使用combosequence
作为初始化器,因为它只接受sf::Keyboard::Key
类型。如果它要求initializer_list
,这是有道理的,但这对我来说似乎很奇怪。对于其他标准容器,我可以使用 表示法调用复制构造函数。这是std::array
的怪癖吗?或者可能是我的 clang 中的一个错误?
以防万一,这是我的 clang 版本:
Debian clang version 3.5.0-10 (tags/RELEASE_350/final) (based on LLVM 3.5.0)
Target: x86_64-pc-linux-gnu
Thread model: posix
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/4.9
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/4.9.2
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/4.9
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/4.9.2
Selected GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/4.9
Candidate multilib: .;@m64
Selected multilib: .;@m64
【问题讨论】:
这是std::array
的一个怪癖,也是 C++14 中的一个缺陷。该容器必须是一个聚合容器,并且在 C++14 中使用单个元素进行列表初始化是有缺陷的。见open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#1467
@dyp 好的,谢谢。这是否意味着我可以期待在 C++17 中看到修复?
建议的决议确实出现在最近的草稿中,所以我希望它会在下一个标准修订版中得到修复。
顺便说一句,这与std::initializer_list
无关。这是一个类模板,旨在简化 braced-init-lists 中的某些初始化。后者是 ..
初始化构造的语法名称。
【参考方案1】:
您在 C++ 中遇到了一个缺陷:从单个元素进行列表初始化。 C++11 和 C++14 国际标准中规定的行为令人惊讶。下面我会参考C++14。
std::array
的模板实例是聚合[array.overview]/2。因此,当从 braced-init-list 初始化 std::array
对象时,aggregate-initialization 将不加选择地执行初始化器的数量[dcl.init.列表]/3.1。由于某些构造的要求(例如,来自一对迭代器),其他容器类不能聚合。
聚合初始化(可能递归地)初始化来自初始化器的数据成员。在您的情况下,它将尝试从初始化程序 sequence
(属于同一类型)初始化 std::array<sf::Keyboard::Key, N>
的第一个数据成员。对于std::array
我知道的所有实现,std::array
的第一个数据成员是一个 C 样式的数组。然后列表初始化将尝试从原始初始化程序初始化该数组的第一个元素:sequence
。
例子:
struct aggregate
int m[2];
;
aggregate x = 0, 1;
assert(x.m[0] == 0 && x.m[1] == 1);
aggregate yx; // error: cannot convert `aggregate` to `int`
最后一行的初始化会尝试从x
初始化y.m[0]
。
CWG issue 1467 描述了这个和一个相关的问题,没有初始化器时的列表初始化。提议的决议为列表初始化引入了一个(又一个)特殊情况,涵盖了 OP 中的问题。引用最近的 github 草案,[dcl.init.list]/3.1
如果
T
是一个类类型并且初始化列表有一个元素 键入 cvU
,其中U
是T
或派生自T
的类,对象是 从该元素初始化(通过复制初始化 复制列表初始化,或直接初始化 直接列表初始化)。
最近草稿中的聚合初始化具有较低的“优先级”(3.3),即只有在不满足上述条件时才会执行。
最新版本的 g++ (5.0) 和 clang++ (3.7.0) 甚至在 C++11 模式下也实现了建议的解决方案。
【讨论】:
以上是关于c++11数组初始化不会调用复制构造函数的主要内容,如果未能解决你的问题,请参考以下文章