C++11 数组初始化不会调用复制构造函数

c++11 array initialization won't call copy constructor

提问人:Brian Rodriguez 提问时间:4/4/2015 最后编辑:dypBrian Rodriguez 更新时间:4/4/2015 访问量:667

问:

我正在制作一个小类,它使用一个按其大小模板化的数组。这里有一些代码...

.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), progress{begin(combo)}, callback{fn}
{

}

在构造函数的成员初始化中,我不能用作初始值设定项,因为它只接受类型。如果它要求一个,这是有道理的,但这对我来说似乎很奇怪。对于其他标准容器,我可以使用 {} 表示法调用复制构造函数。这是怪癖吗?或者也许是我的叮当声中的错误?combo{sequence}sf::Keyboard::Keyinitializer_liststd::array

以防万一有帮助,这是我的 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
C++ C++11 构造函数 Uniform-Initialization StdArray

评论

1赞 dyp 4/4/2015
这是 C++14 的一个怪癖和缺陷。该容器必须是聚合,并且使用单个元素的列表初始化在 C++14 中是有缺陷的。查看 open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#1467std::array
0赞 Brian Rodriguez 4/4/2015
@dyp好的,谢谢。这是否意味着我可以期待在 C++17 中看到修复?
0赞 dyp 4/4/2015
拟议的决议确实出现在最近的草案中,因此我希望它在下一次标准修订中得到修复。
0赞 dyp 4/4/2015
顺便说一句,这与.这是一个类模板,旨在简化 braced-init-lists 中的某些初始化。后者是初始值设定项构造的语法名称。std::initializer_list{..}

答:

3赞 dyp 4/4/2015 #1

您在 C++ 中遇到了一个缺陷:来自单个元素的列表初始化。C++11 和 C++14 国际标准中规定的行为令人惊讶。我将在下面参考 C++14。

的模板实例化是聚合 [array.overview]/2。因此,当从 braced-init-list 初始化对象时,将不分选择地执行初始值设定项数量的聚合初始化 [dcl.init.list]/3.1。由于某些构造的要求(例如,来自一对迭代器),其他容器类不能是聚合。std::arraystd::array

聚合初始化初始化 (可能以递归方式) 初始化设定项中的数据成员。在您的情况下,它将尝试从初始值设定项(相同类型)初始化的第一个数据成员。对于我所知道的所有实现,第一个数据成员是 C 样式数组。然后,list-initialization 将尝试从原始初始值设定项初始化该数组的第一个元素:。std::array<sf::Keyboard::Key, N>sequencestd::arraystd::arraysequence

例:

struct aggregate
{
    int m[2];
};

aggregate x = {0, 1};
assert(x.m[0] == 0 && x.m[1] == 1);

aggregate y{x}; // error: cannot convert `aggregate` to `int`

最后一行中的初始化将尝试从 初始化。y.m[0]x


CWG 第 1467 期描述了这个问题和相关问题,即在没有初始值设定项时进行列表初始化。拟议的决议引入了一个(又一个)列表初始化的特例,涵盖了 OP 中的问题。 引用最近的 github 草案 [dcl.init.list]/3.1

如果 是类类型,并且初始值设定项列表具有 键入 cv ,其中 是 或派生自的类,对象是 从该元素初始化(通过复制初始化 copy-list-initialization,或通过直接初始化 direct-list-initialization)。TUUTT

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


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