Неоднозначная ошибка С++ 11 с двумя операторами (одно значение lvalue, второе значение r)

ОБНОВЛЕНИЕ внизу.

Я хочу, чтобы make void tree::operator = ( tree t ) использовал значение rvalue one (в этом случае, как правило, я хочу обрабатывать их по-разному из-за эффективности)

Я закодировал его, использовал std::move, чтобы убедиться, что он будет использовать rvalue, но компилятор говорит, что не знает, какой оператор выбрать. Разве он не должен выбрать это, используя rvalue?

Код:

void tree::operator = ( tree&& t )
{
  std::swap(this->pntr, t.pntr);
}

void tree::operator = ( tree t )
{
  *this = std::move(t);
}

Ошибка компилятора:

tree.cpp:23:9: error: use of overloaded operator '=' is ambiguous (with operand types 'tree' and 'typename remove_reference<tree &>::type' (aka 'tree'))
  *this = std::move(t);
  ~~~~~ ^ ~~~~~~~~~~~~
tree.cpp:16:12: note: candidate function
void tree::operator = ( tree&& t )
           ^
tree.cpp:21:12: note: candidate function
void tree::operator = ( tree t )
           ^
1 error generated.

Я использую clang-503.0.38. (но с gcc 4.8 такая же ошибка)

ОБНОВЛЕНИЕ

Хорошо, теперь у меня есть:

tree& tree::operator = ( tree&& t )
{
  std::swap(this->pntr, t.pntr);
}

tree& tree::operator = ( const tree & t )
{
  *this = tree(t); // tree:tree( const tree& t )
}

И это работает. Завтра я опубликую то, что я узнал из этого, как ответ.


person Marqin    schedule 27.03.2014    source источник
comment
На самом деле у вас проблема похуже, чем ошибка компиляции. Если бы у вас не было ошибки, у вас был бы бесконечный рекурсивный вызов функции в этом присваивании, поскольку присваивание вызывало бы себя для выполнения присваивания.   -  person Some programmer dude    schedule 27.03.2014
comment
Вы должны подумать о том, чтобы прочитать [это] [1]. [1]: stackoverflow.com/questions/9747406/   -  person galop1n    schedule 27.03.2014


Ответы (2)


Вы можете закодировать только одну версию tree::operator=(tree t), которая реализована с помощью идиомы копирования и замены, в то время как конструктор копирования и конструктор перемещения предоставляются.

Таким образом, клиент класса может выбрать назначение копирования, например:

tree1 = tree2;

, в то время как также можно выбрать назначение перемещения, например:

tree1 = std::move(tree2);
person cbel    schedule 27.03.2014
comment
Я хочу иметь как копирование, так и перемещение заданий. Это C++11, у нас есть целевое перемещение. - person Marqin; 28.03.2014
comment
Код, который вы обновили, может работать. Но копирование и замена считается лучшей реализацией, пожалуйста, прочитайте эту ссылку . С этой идиомой вам нужен только один, а не два оператора =(). - person cbel; 28.03.2014
comment
Мне нужны разные операторы, чтобы по-разному обрабатывать rvalue и lvalue. Это мое задание класса С++. - person Marqin; 28.03.2014

В вашем коде есть несколько проблем:

  • Оператор присваивания должен возвращать ссылку на сам объект. То есть должен возвращать tree&, а не void.

  • И tree::operator=( tree other ), и tree::ooperator( tree&& other ) могут принимать значения r, поэтому перегрузки неоднозначны. Если вам нужно/хотите только назначение rvalue, ваше назначение rvalue на основе копирования и подкачки является правильным, но если вам нужны и rvalue, и lvalues, вы должны указать только назначение значения и использовать для него идиому копирования и подкачки:

    tree& tree::operator=( tree other )
    {
        using std::swap; //Enable ADL (Not strictly neccesary, but good practice)
    
        swap( *this , other );
    
        return *this;
    }
    

    Обратите внимание, что вы должны написать пользовательскую функцию swap(), чтобы прервать потенциальную бесконечную рекурсию в реализации std::swap() по умолчанию. Прочитайте эту тему, чтобы узнать больше об идиоме копирования и подкачки.

person Manu343726    schedule 27.03.2014
comment
Я хочу иметь как копирование, так и перемещение заданий. Это C++11, у нас есть целевое перемещение. - person Marqin; 28.03.2014
comment
@markin Как я уже сказал в ответе, присваивание значения может принимать как rvalue, так и lvalue: Вот почему ваша реализация неоднозначна, и только присваивание значения необходимо для одновременной реализации присваивания rvalue и lvalue. - person Manu343726; 28.03.2014
comment
См. Обновление. Я исправил задание копирования. Но также спасибо за указание на то, что пустое присваивание неверно. - person Marqin; 28.03.2014
comment
@Marqin, не делай этого. Смысл присваивания по значению и идиомы копирования и подкачки состоит в том, чтобы уменьшить дублирование кода и позволить компилятору выполнять лучшую оптимизацию. Предоставьте только назначение по значению вместо значения rvalue и значения lvalue, которое копирует ввод и вызывает назначение rvalue. Если вы хотите скопировать, позвольте компилятору решить, как копировать (если это необходимо, что не в данном случае), вместо того, чтобы делать это вручную. Это ключевой момент передачи по значению как для rvalue, так и для lvalue: позволить компилятору выполнять копирование ellisions всякий раз, когда это возможно - person Manu343726; 28.03.2014
comment
Мне нужны разные операторы, чтобы по-разному обрабатывать rvalue и lvalue. Это мое задание класса С++. Я только что сделал так, чтобы SO использовал значение rvalue по lvalue. Но в моей реализации lvalue one в некоторых случаях работает эффективнее, чем rvalue one, поскольку позволяет избежать ненужного выделения памяти. - person Marqin; 28.03.2014