初始化成员数组对象,避免移动构造函数 [英] Initialization of member array objects avoiding move constructor

查看:230
本文介绍了初始化成员数组对象,避免移动构造函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图创建和初始化一个类,包含一个非平凡类的成员数组,它包含一些状态和(在一些角落) 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}; //想让每个foo有不同的状态
public:
// ...
};

如果我理解编译器输出正确,这似乎成员数组,但是构造临时并调用move / copy构造函数,它随后在子类中调用move构造函数,并且恰好在 std :: atomic_flag 。我得到的编译器输出(gcc 4.8.1)是:

  [...]错误: foo :: foo(foo&&)'
注意:'foo :: foo(foo&& amp;)'被隐式删除,因为默认定义是不成形的
error:use of deleted函数'spinlock :: spinlock(spinlock&)'
note:'spinlock :: spinlock(spinlock&&&)'被隐式删除,因为[...]
错误:使用删除的函数'std :: atomic_flag :: atomic_flag(const std :: atomic_flag&)'
在[...] / i686-w64-mingw32 / 4.8.1 / include / c ++ / atomic:41:0
[etc]



如果我删除数组,而只是放一个 foo 成员在 bar 中,我可以使用标准构造函数初始化程序或使用新的in-declaration初始化正确初始化,没有任何问题。与成员数组做同样的事情失败了与上述错误,无论我尝试。



我不介意数组元素显然构造为临时和然后移动,而不是直接构造,但事实,它不编译明显是一个showstopper。



有一种方式,我强制编译器构造

解决方案

这里是一个暴露问题的最小示例:

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

int main()
{
noncopyable f0 = {1};
noncopyable f1 = 1;虽然两个初始化 f0



$ b $ $ c>和 f1 具有相同的形式(都是复制初始化), f0 使用直接调用构造函数的列表初始化,而 f1 的初始化基本上等效于 foo f1 = foo(1); (创建一个临时文件并将其复制到 f1 )。



数组案例:

  noncopyable f0 [] = {{1},{2},{3},{4}}; 
noncopyable f1 [] = {1,2,3,4};

聚合初始化定义为成员的复制初始化[dcl.init.aggr] / 2


每个成员都是从相应的 initializer-clause 初始化的。


因此, f1 本质上说 f1 [0] = 1,f1 [1] = 2,.. (此符号应描述数组元素的初始化),其具有与上述相同的问题。 OTOH, f0 [0] = {1} (作为初始化)再次使用列表初始化,它直接调用构造函数并且不在语义上创建一个临时。 / p>




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



编辑:不工作,从braced-init-list复制初始化可能不使用显式构造函数。也就是说,对于

  struct expl 
{
explicit expl
};

初始化 expl e = {1}; 是形态不良。出于同样的原因, expl e [] = {{1}}; 是错误的。 expl e {1}; 格式正确。


I'm trying to create and initialize a class that contains a member array of a non-trivial class, which contains some state and (around some corners) std::atomic_flag. As of C++11 one should be able to initialize member arrays.

The code (stripped down to minimum) looks like this:

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:
    //...
};

If I understand the compiler output correctly, this seems not to construct the member array, but to construct temporaries and invoke the move/copy constructor, which subsequently calls move constructors in sub-classes, and that one happens to be deleted in std::atomic_flag. The compiler output that I get (gcc 4.8.1) is:

[...] error: use of deleted function 'foo::foo(foo&&)'
note: 'foo::foo(foo&&)' is implicitly deleted because the default definition would be ill-formed
error: use of deleted function 'spinlock::spinlock(spinlock&&)'
note: 'spinlock::spinlock(spinlock&&)' is implicitly deleted because [...]
error: use of deleted function 'std::atomic_flag::atomic_flag(const std::atomic_flag&)'
In file included from [...]/i686-w64-mingw32/4.8.1/include/c++/atomic:41:0
[etc]

If I remove the array and instead just put a single foo member inside bar, I can properly initialize it using standard constructor initializers, or using the new in-declaration initialization, no problem whatsoever. Doing the same thing with a member array fails with the above error, no matter what I try.

I don't really mind that array elements are apparently constructed as temporaries and then moved rather than directly constructed, but the fact that it doesn't compile is obviously somewhat of a showstopper.

Is there a way I either force the compiler to construct (not move) the array elements, or a way I can work around this?

解决方案

Here's a minimal example exposing the problem:

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

int main()
{
    noncopyable f0 = {1};
    noncopyable f1 = 1;
}

Although the two initializations of f0 and f1 have the same form (are both copy-initialization), f0 uses list-initialization which directly calls a constructor, whereas the initialization of f1 is essentially equivalent to foo f1 = foo(1); (create a temporary and copy it to f1).

This slight difference also manifests in the array case:

noncopyable f0[] = {{1}, {2}, {3}, {4}};
noncopyable f1[] = {1, 2, 3, 4};

Aggregate-initialization is defined as copy-initialization of the members [dcl.init.aggr]/2

Each member is copy-initialized from the corresponding initializer-clause.

Therefore, f1 essentially says f1[0] = 1, f1[1] = 2, .. (this notation shall describe the initializations of the array elements), which has the same problem as above. OTOH, f0[0] = {1} (as an initialization) uses list-initialization again, which directly calls the constructor and does not (semantically) create a temporary.


You could make your converting constructors explicit ;) this could avoid some confusion.

Edit: Won't work, copy-initialization from a braced-init-list may not use an explicit constructor. That is, for

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

the initialization expl e = {1}; is ill-formed. For the same reason, expl e[] = {{1}}; is ill-formed. expl e {1}; is well-formed, still.

这篇关于初始化成员数组对象,避免移动构造函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆