Статическое утверждение для конструктора перемещения, отличного от конструктора копирования

представьте, что у меня есть класс A это дешево для перемещения и дорого для копирования. Это может выглядеть как

class A {
  public: 
    [...]

  private:
    HeavyClass m;
};

для этого класса я хотел бы статическую проверку того, что класс (1) движение конструктивное и (2) не просто использует конструктор копирования для построения перемещения, независимо от того, был ли явно объявлен конструктор move или нет.

это возможно?

почему я хотел бы этого, рассмотрим следующий пример: Сначала класс автоматически генерирует конструктор перемещения и ведет себя так, как требуется. Затем кто-то изменяет класс и добавляет деструктор, что приводит к тому, что конструктор перемещения неявно генерируется и вместо него используется конструктор копирования.

следовательно, a static_assert было бы идеально, но, кажется, никто из is_move_constructible или is_trivially_move_constructible полезно здесь.

кроме того, я знаю, что можно иметь A(A&&) = default; для всех таких классов, но решение с static_assert будет будьте чище и разрешите выполнять проверку вне определения класса (f.Я. в других проектах, опирающихся на этот класс).

редактировать:
Я не хочу запрещать создание копий, я просто хотел бы убедиться, что конструктор move не использует его...

3 ответов


Если вы можете изменить A к абстракции, вы можете сделать следующее:

template <bool>
struct MoveOnly {
    MoveOnly() = default;
    ~MoveOnly() = default;
    MoveOnly(const MoveOnly&) = delete;
    MoveOnly(MoveOnly&&) = default;
    MoveOnly& operator=(const MoveOnly&) = delete;
    MoveOnly& operator=(MoveOnly&&) = default;
};

template <> struct MoveOnly<false> {};

template <bool check = false>
class A_Impl : MoveOnly<check> {
public: 
    // ... as ~A_Impl() {}
    // ...
private:
    HeavyClass m;
};

using A = A_Impl<false>; // Normal case

// The check
static_assert(std::is_move_constructible<A_Impl<true>>::value, "");

демо


генерация конструктора копирования и оператора назначения копирования -устаревший если деструктор объявлен. Нет необходимости в статических утверждениях или шаблонах, это часть современного C++.

решение является просто для включения предупреждения

также превратить предупреждения в ошибки, если вы еще не сделали этого.

таким образом, вам не нужно помнить, чтобы добавить статические утверждения во всем. Кроме того, вы все еще можете добавить деструкторы, имеют подвижные члены и наследовать от не-moveables пока вы не копируете и не перемещаете какие-либо экземпляры таких объектов. Нет смысла в ограничениях, когда они не используются.

С помощью этой настройки вы можете попытаться добавить этот код в свой класс (типичная регрессия)

virtual ~A() = default;

если вы пытаетесь переместить или скопировать экземпляр A теперь компиляция завершится неудачей.

пример сообщение от clang-3.9 -Werror -Wdeprecated

main.cpp:13:13: error: definition of implicit copy assignment
operator for 'A' is deprecated because it has a user-declared 
destructor [-Werror,-Wdeprecated]
    virtual ~A() = default;
            ^
main.cpp:21:7: note: implicit copy assignment operator for
'A' first required here
    b = std::move(a);
      ^
1 error generated.

если вы создаете экземпляр A и просто передайте его вокруг const reference, тогда никаких жалоб от вас компилятор.


Это полностью общая проблема обслуживания. В идеале мы хотим найти общее решение для этого. вручную добавление утверждает, что это плохое решение. Он подвержен ошибкам, легко забывается, занимает много времени и делает код менее читаемым.

одним из общих решений было бы использование статического анализатора для обеспеченияправило ноль для всех классов.

ссылка на так вопрос об этом. Однако добавление параметров компилятора as описано в моем другом ответе является лучшим решением, если компилятор поддерживает его.