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

时间:2015-04-03 22:38:24

标签: c++ c++11 constructor uniform-initialization stdarray

我正在创建一个使用按其大小模板化的数组的类。这是一些代码......

.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::Key类型。如果它要求initializer_list,这是有道理的,但这对我来说似乎很奇怪。使用其他标准容器,我可以使用{}表示法调用复制构造函数。这是std::array的怪癖吗?或者也许是我的铿锵声中的一个错误?

以防它有帮助,这是我的铿锵版:

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

1 个答案:

答案 0 :(得分:3)

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

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

聚合初始化初始化(可能是递归地)初始化器中的数据成员。在您的情况下,它将尝试从初始化程序std::array<sf::Keyboard::Key, N>(具有相同类型)初始化sequence的第一个数据成员。对于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 y{x}; // error: cannot convert `aggregate` to `int`

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


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

  

如果T是类类型,并且初始化列表具有单个元素   输入 cv U,其中UT或从T派生的类,对象是   从该元素初始化(通过复制初始化)   copy-list-initialization,或者直接初始化   直接一览初始化)。

最近草稿中的聚合初始化具有较低的优先级&#34; (3.3),即只有在不符合上述条件的情况下才会执行。


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