C++11 数组初始化不会调用复制构造函数
c++11 array initialization won't call copy constructor
我正在制作一个小 class,它使用一个根据其大小模板化的数组。这是一些代码...
.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
的怪癖吗?或者我的 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
您遇到了 C++ 中的一个缺陷:单个元素的列表初始化。 C++11 和 C++14 国际标准中指定的行为令人惊讶。我将在下面参考 C++14。
std::array
的模板实例是聚合 [array.overview]/2。因此,当从 braced-init-list 初始化 std::array
对象时,aggregate-initialization 将不加区别地执行初始化器的数量[dcl.init.list]/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 y{x}; // error: cannot convert `aggregate` to `int`
最后一行的初始化将尝试从 x
.
初始化 y.m[0]
CWG issue 1467 描述了这个问题和一个相关问题,即在没有初始化程序时进行列表初始化。提议的决议引入了一个(又一个)列表初始化特例,涵盖了 OP 中的问题。引用最近的 github 草稿,[dcl.init.list]/3.1
If T
is a class type and the initializer list has a single element of
type cv U
, where U
is T
or a class derived from T
, the object is
initialized from that element (by copy-initialization for
copy-list-initialization, or by direct-initialization for
direct-list-initialization).
聚合初始化在最近的草稿中具有较低的"priority"(3.3),即只有在不满足上述条件时才会执行。
最新版本的 g++ (5.0) 和 clang++ (3.7.0) 即使在 C++11 模式下也实现了建议的解决方案。
我正在制作一个小 class,它使用一个根据其大小模板化的数组。这是一些代码...
.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
的怪癖吗?或者我的 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
您遇到了 C++ 中的一个缺陷:单个元素的列表初始化。 C++11 和 C++14 国际标准中指定的行为令人惊讶。我将在下面参考 C++14。
std::array
的模板实例是聚合 [array.overview]/2。因此,当从 braced-init-list 初始化 std::array
对象时,aggregate-initialization 将不加区别地执行初始化器的数量[dcl.init.list]/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 y{x}; // error: cannot convert `aggregate` to `int`
最后一行的初始化将尝试从 x
.
y.m[0]
CWG issue 1467 描述了这个问题和一个相关问题,即在没有初始化程序时进行列表初始化。提议的决议引入了一个(又一个)列表初始化特例,涵盖了 OP 中的问题。引用最近的 github 草稿,[dcl.init.list]/3.1
If
T
is a class type and the initializer list has a single element of type cvU
, whereU
isT
or a class derived fromT
, the object is initialized from that element (by copy-initialization for copy-list-initialization, or by direct-initialization for direct-list-initialization).
聚合初始化在最近的草稿中具有较低的"priority"(3.3),即只有在不满足上述条件时才会执行。
最新版本的 g++ (5.0) 和 clang++ (3.7.0) 即使在 C++11 模式下也实现了建议的解决方案。