初始化成员数组对象,避免移动构造函数 [英] Initialization of member array objects avoiding move constructor
问题描述
我试图创建和初始化一个类,包含一个非平凡类的成员数组,它包含一些状态和(在一些角落) 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。
有一种方式,我强制编译器构造
这里是一个暴露问题的最小示例:/ p>
struct noncopyable
{
noncopyable(int){};
noncopyable(noncopyable const&)= delete;
};
int main()
{
noncopyable f0 = {1};
noncopyable f1 = 1;虽然两个初始化 f0 $ c <$>
$ 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屋!