Gcc 避免移动构造函数的成员数组对象初始化

Gcc 避免移动构造函数的成员数组对象初始化,gcc,c++11,initialization,move,member-variables,Gcc,C++11,Initialization,Move,Member Variables,我试图创建并初始化一个类,该类包含一个非平凡类的成员数组,该类包含一些状态和(在某些角落)std::atomic_flag。从C++11开始,应该能够初始化成员数组 代码(精简到最小值)如下所示: class spinlock { std::atomic_flag flag; bool try_lock() { return !flag.test_and_set(std::memory_order_acquire); } public: spinlock() : flag

我试图创建并初始化一个类,该类包含一个非平凡类的成员数组,该类包含一些状态和(在某些角落)
std::atomic_flag
。从C++11开始,应该能够初始化成员数组

代码(精简到最小值)如下所示:

class spinlock
{
    std::atomic_flag flag;
    bool try_lock() { return !flag.test_and_set(std::memory_order_acquire); }
public:
    spinlock() : flag(ATOMIC_FLAG_INIT){};
    void lock()     { while(!try_lock()) ; }
    void unlock()   { flag.clear(std::memory_order_release); }
};

class foo
{
    spinlock lock;
    unsigned int state;
public:
    foo(unsigned int in) : state(in) {}
};

class bar
{
    foo x[4] = {1,2,3,4}; // want each foo to have different state
public:
    //...
};
如果我正确理解了编译器的输出,这似乎不是用来构造成员数组,而是用来构造临时数组并调用move/copy构造函数,后者随后在子类中调用move构造函数,并且在
std::atomic_flag
中删除了一个构造函数。我得到的编译器输出(gcc 4.8.1)是:

如果我移除数组,而只是将单个
foo
成员放在
bar
中,我可以使用标准构造函数初始值设定项或使用新的in声明初始化来正确初始化它,没有任何问题。无论我如何尝试,对成员数组执行同样的操作都会因上述错误而失败

我并不介意数组元素显然是作为临时元素构造的,然后移动而不是直接构造的,但是它不编译的事实显然有点像是一个showstopper


有什么方法可以强制编译器构造(而不是移动)数组元素,或者有什么方法可以解决这个问题?

下面是一个暴露问题的简单示例:

struct noncopyable
{
    noncopyable(int) {};
    noncopyable(noncopyable const&) = delete;
};

int main()
{
    noncopyable f0 = {1};
    noncopyable f1 = 1;
}
尽管
f0
f1
的两个初始化形式相同(都是复制初始化),
f0
使用直接调用构造函数的列表初始化,而
f1
的初始化本质上等同于
foo f1=foo(1)(创建一个临时文件并将其复制到
f1

这种细微的差别也体现在数组情况中:

noncopyable f0[] = {{1}, {2}, {3}, {4}};
noncopyable f1[] = {1, 2, 3, 4};
聚合初始化定义为成员的复制初始化[dcl.init.aggr]/2

每个成员都是从相应的初始值设定项子句复制初始化的

因此,
f1
基本上表示
f1[0]=1,f1[1]=2,…
(此符号应描述数组元素的初始化),其问题与上述相同。OTOH,
f0[0]={1}
(作为一个初始化)再次使用列表初始化,它直接调用构造函数,而不(从语义上)创建临时的


您可以使转换构造函数显式这可以避免一些混乱

编辑:不起作用,从带括号的初始化列表复制初始化可能不使用显式构造函数。就是

struct expl
{
    explicit expl(int) {};
};

初始化
exple={1}格式不正确。出于同样的原因,
exple[]={{1}格式不正确<代码>解释{1}仍然是格式良好的。

问题似乎与成员数组无关,而是与聚合初始化[dcl.init.aggr]/2“每个成员都是从相应的初始化器子句中复制初始化的。”似乎您是对的,如果我在
main
中创建并初始化
foo
s的数组,会生成相同的错误。因此,如果这是绑定到聚合初始化的,我唯一的选择就是向
foo
添加一个默认构造函数,并创建一个默认对象数组,然后稍后手动更改状态?使用更多大括号:
foox[4]={{{1},{2},{3},{4}我可能需要一段时间来放下眉毛,然后才能解释为什么这样做有效..这样做有效!想回答这个问题吗?:)一点也不。。。“可以使转换构造函数显式”——有趣的是,这是我尝试过的事情之一,但我不知道为什么它不起作用。这只会给出不同的“从初始值设定项列表转换为[…]将使用显式构造函数”错误。关于列表初始化的解释非常有说服力(有趣的是,当有人解释时,完全不可理解的事情突然变得如此明显)。非常感谢:)@Damon我现在认为
explicit
帮不上忙;这可能会导致这样一个问题:是否有任何方法可以使用显式的构造函数初始化不可复制、不可更改的类型数组。你也看到了,这确实是一个非常有趣的问题。事实上,你会认为一定有办法,因为这些财产都不是特别“不寻常”或“禁止的”,而且似乎没有理由不允许这样做。相反,显然有一些完全合法的构造是不能使用的,这看起来非常奇怪,因为您无法使用定义良好的状态创建它们(您可以将默认构造后的状态设置为默认状态,但是meh)。
struct expl
{
    explicit expl(int) {};
};