Почему конструктор перемещения влияет на is_assignable? - PullRequest
0 голосов
/ 21 декабря 2018

Только что пришло от is_assignable и std :: unique_ptr .@Angew говорит мне, что, поскольку std::unique_ptr<int, do_nothing> и std::unique_ptr<int> - это разные типы, поэтому static_assert(not std::is_assignable<std::unique_ptr<int>, std::unique_ptr<int, do_nothing>>::value, "");.Итак, я попытался:

template<typename T, typename D>
struct MoveAssignOnly_V2
{
    MoveAssignOnly_V2&
    operator=(MoveAssignOnly_V2&)
        = delete;

    MoveAssignOnly_V2&
    operator=(MoveAssignOnly_V2&&) noexcept
    {}
};

int main()
{
      static_assert(not std::is_assignable_v<MoveAssignOnly_V2<int, float>,
                 MoveAssignOnly_V2<int, double>>);
}

Да, потому что MoveAssignOnly_V2<int, float> и MoveAssignOnly_V2<int, double> - это два разных типа, поэтому они не присваиваются.

Но , когда я добавляю ctor хода:

template<class U, class E>
MoveAssignOnly_V2(MoveAssignOnly_V2<U, E>&& m) noexcept {}

static_assert fail! (как gcc, так и clang).

Вопрос здесь: почему конструктор перемещения влияет на is_assignable?

Обновлено

Причина, по которой я добавляю этот конструктор, состоит в том, что я обнаружил, что std::unique_ptr имеет

template< class U, class E >
unique_ptr( unique_ptr<U, E>&& u ) noexcept;

, что меня немного смущает: как это может быть невозможно назначить сейчас, когда у него есть такой ctor?поэтому я попытался добавить такой ctor к MoveAssignOnly_V2 и опубликовать этот вопрос.Эти два ответа хороши, однако, все еще не может объяснить, почему std::unique_ptr нельзя назначить, когда у него есть и назначение перемещения, и этот шаблонный конструктор.

Ответы [ 2 ]

0 голосов
/ 21 декабря 2018

То, что вы добавили здесь:

template<class U, class E>
MoveAssignOnly_V2(MoveAssignOnly_V2<U, E>&& m) noexcept {}

- это не только конструктор перемещения, но и шаблонный конструктор, который может создать MoveAssignOnly_V2<T, D> из любого MoveAssignOnly_V2<U, E>.

. Таким образом, конструируется MoveAssignOnly_V2<int, float> с MoveAssignOnly_V2<int, double>> в порядке.

0 голосов
/ 21 декабря 2018

Возьмите этот код:

MoveAssignOnly_V2<int, float> lhs;
MoveAssignOnly_V2<int, double> rhs;
lhs = stdL::move(rhs);

Когда конструктор преобразования (обратите внимание, что он не конструктор перемещения) не существует, нет способа присвоить rhs в lhs.

Однако, когда вы добавляете шаблон конструктора, теперь есть способ преобразовать rhs в тип MoveAssignOnly_V2<int, float> (который создает временный объект этого типа).Затем можно переместить-назначить из этого временного в lhs.

Это тот же принцип, что и:

double lhs = 3.14;
float rhs = 42.f;
lhs = std::move(rhs);

Для обращения к обновлению в вопросе:

Вы не можете использовать только объявления функций, вам необходимо прочитать полную спецификацию (в стандарте или подходящая ссылка ).Цитирование связанной ссылки о конструкторе преобразования std::unique_ptr:

Этот конструктор участвует в разрешении перегрузки только в том случае, если все следующее верно:

a) unique_ptr<U, E>::pointer неявнопреобразуется в pointer
b) U не является типом массива
c) Либо Deleter является ссылочным типом, а E является тем же типом, что и D, или Deleter не являетсяссылочный тип и E неявно преобразуются в D

Так что, как вы можете видеть, преобразователь unique_ptr должен быть реализован так, чтобы он был активен, только если удалитель исходного кода можетбыть преобразован в целевой.Это в основном то же правило, что и при назначении ходов в предыдущем вопросе.

...