Constexpr инициализация массива не копируемых объектов? - PullRequest
2 голосов
/ 29 апреля 2019

Я хотел бы иметь класс 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;
}

Однако мне бы очень хотелось иметь 3 объекта типа A в виде массива. Если я попробую простой подход, как это:

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 подвижным?

EDIT:

По предложению @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. Если я удаляю деструктор, тогда он работает, но что, если деструктор должен быть там? Есть ли решение этой конкретной проблемы инициализации массива или мне здесь не повезло?

1 Ответ

4 голосов
/ 29 апреля 2019

Строго говоря, инициализация экземпляра A из A{1} - это copy- (или перемещение) инициализации. Большинство компиляторов исключают функцию копирования / перемещения и даже не удосуживаются вызвать конструктор копирования / перемещения, но только начиная с C ++ 17 наличие конструктора копирования / перемещения фактически не требуется.

В качестве обходного пути вы можете удалить explicit из конструктора A s и создать на месте A s:

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 действительно неподвижен и не подлежит копированию, скажем, что у него есть деструктор.

Единственный возможный обходной путь, о котором я могу подумать, - это ядерный вариант .

#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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...