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&lt;sf::Keyboard::Key, N&gt; 的第一个数据成员。对于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 是一个类类型并且初始化列表有一个元素 键入 cv U,其中 UT 或派生自 T 的类,对象是 从该元素初始化(通过复制初始化 复制列表初始化,或直接初始化 直接列表初始化)。

最近草稿中的聚合初始化具有较低的“优先级”(3.3),即只有在不满足上述条件时才会执行。


最新版本的 g++ (5.0) 和 clang++ (3.7.0) 甚至在 C++11 模式下也实现了建议的解决方案。

【讨论】:

以上是关于c++11数组初始化不会调用复制构造函数的主要内容,如果未能解决你的问题,请参考以下文章

在构造函数初始化列表中初始化成员数组(C++11之前)

为啥编译器在尝试使用 C++11 样式初始化对象数组时隐式删除​​构造函数

函数调用或构造函数调用中的 C++ 数组初始化

为啥就地成员初始化在 C++11 中使用复制构造函数?

C++对象数组与对象指针

C ++:数组的构造函数/初始化程序?