Опасность использования виртуальных базовых операторов перемещения, когда они теперь могут использоваться?

Это касается решения проблемы C++ http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html. Резюме:

template<typename T>
struct wrap
{
   wrap() = default;

   wrap(wrap&&) = default;
   wrap(const wrap&) = default;

   T t;
};

struct S {
   S(){}
   S(const S&){}
   S(S&&){}
};

typedef wrap<const S> W;

// Error, defaulted move constructor of "wrap<const S>" is deleted!
W get() { return W(); }

(Проблема заключается в том, что мы получаем ошибку для этого фрагмента, хотя компилятор может просто использовать конструктор копирования "S", как это происходит, когда пользователь явно записывает конструктор перемещения "wrap". Проблема была решена в игнорировать удаленные конструкторы перемещения (и операторы присваивания) во время разрешений перегрузки, следовательно, при необходимости используйте выше конструктор копирования.)

Когда проект резолюции был составлен, был сделан следующий комментарий об этом решении:

Нет дополнительных ограничений для неявных / дефолтных операций перемещения относительно копирования. Это означает, что назначение перемещения в виртуальной базе опасно (и компиляторы, вероятно, должны предупреждать) [...] Но мы решили в Батавии, что не собираемся сохранять весь код C++03 от неявных операций перемещения, и я думаю, Это разрешение обеспечивает значительные преимущества в производительности.

Кто-нибудь может, пожалуйста, описать, какова проблема с виртуальными операторами назначения базовых перемещений?

1 ответ

Рассматривать:

#include <iostream>

struct A
{
    A() = default;
    A(const A&) = default;
    A(A&&) = default;
    A& operator=(const A&) {std::cout << "operator=(const A&)\n"; return *this;}
    A& operator=(A&&) {std::cout << "operator=(A&&)\n"; return *this;}
};

struct B
    : virtual public A
{
};

struct C
    : virtual public A
{
};

struct D
    : public B,
      public C
{
};

int
main()
{
    D d1, d2;
    d1 = std::move(d2);
}

Я считаю, что эта программа должна выводить:

operator=(A&&)
operator=(A&&)

Для меня это на самом деле выводит:

operator=(const A&)
operator=(const A&)

Но я думаю, что это просто лягушка (скомпилированная с -std= C++1y). Если я прав в отношении того, каким должен быть вывод, тогда опасность состоит в том, что оператор присваивания перемещения вызывается дважды. Это безопасно (хотя и потенциально дорого) для оператора назначения копирования, но не для оператора назначения перемещения.

Другие вопросы по тегам