【问题标题】:Constexpr initialization of an array of non-copyable objects?Consexpr 初始化不可复制对象数组?
【发布时间】:2019-04-29 08:13:15
【问题描述】:

我想要一个类B,它有另一个类A 的3 个成员对象。 AB 的构造函数都是 constexprA - 包含在 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,它也会失败。如果我删除析构函数,那么它就可以工作,但是如果析构函数必须在那里呢?这个特定的数组初始化问题是否有解决方案,或者我在这里不走运?

【问题讨论】:

  • 您是否尝试将 C++17 标志添加到您的编译命令:g++ -std=c++17 a.cpp?!使用 g++ 和 clang 为我工作。
  • @lubgr - 这个问题有 c++11 解决方案吗?
  • 如果您可以在A 构造函数中删除explicit,则应该与a{ {1}, {2}, {3} } 一起使用(也适用于C++11/C++14)。

标签: c++ arrays constructor initialization constexpr


【解决方案1】:

严格来说,从A{1} 初始化A 的实例是复制-(或移动)初始化。大多数编译器省略了复制/移动,甚至不打扰调用复制/移动构造函数,但仅从 C++17 开始,实际上不需要存在复制/移动构造函数。

作为一种解决方法,您可以从As 构造函数中删除explicit 并就地构造As:

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;
}

=== EDIT ===(响应问题编辑)

假设 A 确实是不可移动和不可复制的,假设它有一个析构函数。

我能想到的唯一可能的解决方法是nuclear option a.k.a.placement-new:

#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;
}

【讨论】:

  • 确实,这适用于简单的情况,但通常真正的代码有点复杂(;你能看看我编辑的问题吗?
猜你喜欢
  • 2018-02-06
  • 2019-08-25
  • 1970-01-01
  • 2018-06-22
  • 1970-01-01
  • 2017-02-16
  • 2018-09-19
相关资源
最近更新 更多