C ++ Унифицированная семантика Move оператора унифицированного присваивания - PullRequest
19 голосов
/ 18 сентября 2011

РЕДАКТИРОВАТЬ: решено см. В комментариях - не знаю, как пометить как решенное без ответа.

После просмотра видео на канале 9 по семантике Perfect Forwarding / Move в c ++ 0x я был некоторымчто привело к убеждению, что это хороший способ написать новые операторы присваивания.

#include <string>
#include <vector>
#include <iostream>

struct my_type 
{
    my_type(std::string name_)
            :    name(name_)
            {}

    my_type(const my_type&)=default;

    my_type(my_type&& other)
    {
            this->swap(other);
    }

    my_type &operator=(my_type other)
    {
            swap(other);
            return *this;
    }

    void swap(my_type &other)
    {
            name.swap(other.name);
    }

private:
    std::string name;
    void operator=(const my_type&)=delete;  
    void operator=(my_type&&)=delete;
};


int main()
{
    my_type t("hello world");
    my_type t1("foo bar");
    t=t1;
    t=std::move(t1);
}

Это должно позволить присваивать ему и r-значения, и const & s.Создав новый объект с помощью соответствующего конструктора, а затем поменяв содержимое на * this.Мне кажется, это звучит так, потому что никакие данные не копируются больше, чем нужно.А арифметика указателей дешевая.

Однако мой компилятор не согласен.(g ++ 4.6) И я получаю эти ошибки.

copyconsttest.cpp: In function ‘int main()’:
copyconsttest.cpp:40:4: error: ambiguous overload for ‘operator=’ in ‘t = t1’
copyconsttest.cpp:40:4: note: candidates are:
copyconsttest.cpp:18:11: note: my_type& my_type::operator=(my_type)
copyconsttest.cpp:30:11: note: my_type& my_type::operator=(const my_type&) <deleted>
copyconsttest.cpp:31:11: note: my_type& my_type::operator=(my_type&&) <near match>
copyconsttest.cpp:31:11: note:   no known conversion for argument 1 from ‘my_type’ to ‘my_type&&’
copyconsttest.cpp:41:16: error: ambiguous overload for ‘operator=’ in ‘t = std::move [with _Tp = my_type&, typename std::remove_reference< <template-parameter-1-1> >::type = my_type]((* & t1))’
copyconsttest.cpp:41:16: note: candidates are:
copyconsttest.cpp:18:11: note: my_type& my_type::operator=(my_type)
copyconsttest.cpp:30:11: note: my_type& my_type::operator=(const my_type&) <deleted>
copyconsttest.cpp:31:11: note: my_type& my_type::operator=(my_type&&) <deleted>

Я что-то не так делаю?Это плохая практика (я не думаю, что есть способ проверить, назначаете ли вы себя)?Компилятор еще не готов?

Спасибо

Ответы [ 3 ]

24 голосов
/ 18 сентября 2011

Будьте очень осторожны в смысле назначения копирования / обмена. Это может быть неоптимальным, особенно когда применяется без тщательного анализа. Даже если вам нужна строгая безопасность исключений для оператора присваивания, эта функциональность может быть получена иным способом.

Для вашего примера я рекомендую:

struct my_type 
{
    my_type(std::string name_)
            :    name(std::move(name_))
            {}

    void swap(my_type &other)
    {
            name.swap(other.name);
    }

private:
    std::string name;
};

Это позволит вам неявно копировать и перемещать семантику, которая пересылает в std :: string для копирования и перемещения членов. И автор std :: string лучше всех знает, как выполнить эти операции.

Если ваш компилятор еще не поддерживает неявную генерацию перемещения, но поддерживает специальные члены по умолчанию, вы можете сделать это вместо этого:

struct my_type 
{
    my_type(std::string name_)
            :    name(std::move(name_))
            {}

    my_type(const mytype&) = default;
    my_type& operator=(const mytype&) = default;
    my_type(mytype&&) = default;
    my_type& operator=(mytype&&) = default;

    void swap(my_type &other)
    {
            name.swap(other.name);
    }

private:
    std::string name;
};

Вы также можете сделать это, если хотите просто открыто рассказать о своих специальных членах.

Если вы имеете дело с компилятором, который еще не поддерживает дефолтные специальные элементы (или неявные элементы перемещения), то вы можете явно указать, что компилятор должен в конечном итоге установить по умолчанию, когда он станет полностью совместимым с C ++ 11:

struct my_type 
{
    my_type(std::string name_)
            :    name(std::move(name_))
            {}

    my_type(const mytype& other)
        : name(other.name) {}
    my_type& operator=(const mytype& other)
    {
        name = other.name;
        return *this;
    }
    my_type(mytype&& other)
        : name(std::move(other.name)) {}
    my_type& operator=(mytype&& other)
    {
        name = std::move(other.name);
        return *this;
    }

    void swap(my_type &other)
    {
            name.swap(other.name);
    }

private:
    std::string name;
};

Если вам действительно нужна строгая безопасность исключений для назначения, разработайте его один раз и проясните его (отредактируйте, чтобы включить предложение Люка Дантона):

template <class C>
typename std::enable_if
<
    std::is_nothrow_move_assignable<C>::value,
    C&
>::type
strong_assign(C& c, C other)
{
    c = std::move(other);
    return c;
}

template <class C>
typename std::enable_if
<
    !std::is_nothrow_move_assignable<C>::value,
    C&
>::type
strong_assign(C& c, C other)
{
    using std::swap;
    static_assert(std::is_nothrow_swappable_v<C>,  // C++17 only
                  "Not safe if you move other into this function");
    swap(c, other);
    return c;
}

Теперь ваши клиенты могут выбирать между эффективностью (мой тип :: оператор =) или строгой безопасностью исключений, используя strong_assign.

2 голосов
/ 27 октября 2011

Вы внимательно прочитали сообщение об ошибке?Он видит две ошибки, что у вас есть несколько операторов назначения копирования и несколько операторов назначения перемещения.И это совершенно правильно!

Специальные члены должны быть указаны не более одного раза, независимо от того, были ли они по умолчанию, удалены, определены условно или неявно обработаны при исключении.У вас есть два оператора назначения копирования (один принимает my_type, другой - my_type const &) и два оператора назначения перемещения (один принимает my_type, другой - my_type &&).Обратите внимание, что оператор присваивания, который принимает my_type, может обрабатывать ссылки lvalue и rvalue, поэтому он действует как назначение копирования и перемещения.

Сигнатура функции большинства специальных членов имеет несколько форм.Вы должны выбрать один;Вы не можете использовать необычный, а затем удалить обычный, потому что это будет двойное объявление.Компилятор автоматически использует необычно сформированный специальный элемент и не будет синтезировать специальный элемент с обычной подписью.

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

0 голосов
/ 18 сентября 2011

Предполагается, что вы удаляете эти перегрузки оператора присваивания? Разве ваше объявление оператора присваивания не должно быть шаблоном или чем-то еще? Я действительно не понимаю, как это должно работать.

Обратите внимание, что даже если это сработало, реализовав таким образом оператор присваивания перемещения, ресурсы, удерживаемые только что перемещенным объектом, будут высвобождены в момент окончания его времени жизни, а не в точке назначения. Смотрите здесь для более подробной информации:

http://cpp -next.com / Архив / 2009/09 / ваш-следующего присваивания /

...