我想有一个类B
,其中有3个成员对象是另一个类A
。 A
和B
的构造函数均为constexpr
。 A
-包含在B
中-是不可复制且不可移动的。这段代码可以正确构建:
class A
{
public:
constexpr explicit A(int a) {}
A(const A&) = delete;
A(A&&) = delete;
};
class B
{
public:
constexpr B() :
a0{0},
a1{1},
a2{2}
{}
private:
A a0;
A a1;
A a2;
};
int main()
{
B b;
}
但是我真的很想将A
类型的3个对象作为一个数组。如果我尝试这样的简单方法:
class A
{
public:
constexpr explicit A(int a) {}
A(const A&) = delete;
A(A&&) = delete;
};
class B
{
public:
constexpr B() :
a{A{1}, A{2}, A{3}}
{}
private:
A a[3];
};
int main()
{
B b;
}
无法使用:
$ g++ a.cpp
a.cpp: In constructor ‘constexpr B::B()’:
a.cpp:21:22: error: use of deleted function ‘A::A(A&&)’
a{A{1}, A{2}, A{3}}
^
a.cpp:13:2: note: declared here
A(A&&) = delete;
^
a.cpp:21:22: error: use of deleted function ‘A::A(A&&)’
a{A{1}, A{2}, A{3}}
^
a.cpp:13:2: note: declared here
A(A&&) = delete;
^
a.cpp:21:22: error: use of deleted function ‘A::A(A&&)’
a{A{1}, A{2}, A{3}}
^
a.cpp:13:2: note: declared here
A(A&&) = delete;
^
a.cpp:28:2: error: member ‘B::a’ must be initialized by mem-initializer in ‘constexpr’ constructor
}
^
a.cpp:32:7: note: declared here
A a[3];
^
是否可以解决而无需使A
移动吗?
编辑:
如@rustyx所建议,我对代码进行了一些更改,它对于C ++ 11和C ++ 17(使用explicit
)可以正常工作。但是,像往常一样,实际代码要复杂一些。假设A
确实是不可移动且不可复制的,说它具有析构函数。
class A
{
public:
constexpr explicit A(int a) {}
~A() {}
A(const A&) = delete;
A(A&&) = delete;
};
class B
{
public:
constexpr B() :
a{A{1}, A{2}, A{3}}
{}
private:
A a[3];
};
int main()
{
B b;
}
即使使用C ++ 17,此操作也会失败:
g++ a.cpp -std=c++17
a.cpp: In constructor ‘constexpr B::B()’:
a.cpp:14:22: error: use of deleted function ‘A::A(A&&)’
a{A{1}, A{2}, A{3}}
^
a.cpp:7:2: note: declared here
A(A&&) = delete;
^
a.cpp:14:22: error: non-constant array initialization
a{A{1}, A{2}, A{3}}
^
a.cpp:15:3: error: use of deleted function ‘A::A(A&&)’
{}
^
a.cpp:7:2: note: declared here
A(A&&) = delete;
^
如果A
的构造函数不是explicit
,也会失败。如果我删除析构函数,那么它可以工作,但是如果析构函数必须在那里,该怎么办?是否有解决此特定数组初始化问题的方法,或者我在这里不走运?
答案 0 :(得分:4)
严格来说,从A
初始化A{1}
的实例是 copy-(或移动)初始化。大多数编译器都忽略了copy / move,甚至不费心调用copy / move构造函数,但实际上仅从C ++ 17开始才需要存在copy / move构造函数。
作为一种解决方法,您可以从explicit
的构造函数中删除A
并就地构建A
:
class A
{
public:
constexpr A(int a) {}
A(const A&) = delete;
A(A&&) = delete;
};
class B
{
public:
constexpr B() :
a{{1}, {2}, {3}}
{}
private:
A a[3];
};
int main()
{
B b;
}
===编辑=== (响应问题编辑)
让我们说A实际上是不可移动且不可复制的,说它有一个析构函数。
我能想到的唯一可能的解决方法是nuclear option也就是新的展示位置:
#include <memory>
#include <type_traits>
class A
{
public:
constexpr A(int a) {}
A(const A&) = delete;
A(A&&) = delete;
~A() {}
};
class B
{
public:
B() {
new (std::addressof(a[0])) A(1);
new (std::addressof(a[1])) A(2);
new (std::addressof(a[2])) A(3);
}
A& getA(size_t offset) { return reinterpret_cast<A*>(a)[offset]; }
private:
std::aligned_storage<sizeof(A), alignof(A)>::type a[3];
};
int main()
{
B b;
}