C ++ 0x unique_ptr недоразумение? - PullRequest
       4

C ++ 0x unique_ptr недоразумение?

2 голосов
/ 22 августа 2011

In N2812 является примером во Введение , где unique_ptr задано как значение параметра

void push_back2(
  std::list<std::unique_ptr<int>>& l, std::unique_ptr<int> a)
{
  l.push_back(a); // oops: moves from the lvalue 'a', silently!
  l.push_back(a); // oops: 'a' no longer has its original value
}

В статье обсуждается проблема с разрешением перегрузки RValue / LValue, но это не моя точка зрения.

Интересно, если предоставление аргумента std::unique_ptr<int> a по значению не вызывает ошибку компилятора? Это скопировало бы это , верно? И это не разрешено для unique_ptr

Я знаю, что статья довольно старая, возможно, определение unique_ptr изменилось с тех пор. Но, может быть, это просто опечатка, и автор хотел написать std::unique_ptr<int> &a вместо этого?

Мой gcc 4.7.0 согласен со мной, но это не доказательство: -)

void push_back2( std::list<std::unique_ptr<int>>&, std::unique_ptr<int> ) { };
int main() {
  list<unique_ptr<int>> lst;
  unique_ptr<int> num { new int{4} };
  push_back2(lst, num); //ERR: use of deleted function
}

Ответы [ 3 ]

9 голосов
/ 22 августа 2011

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

std::unique_ptr<int> myPtr{ /* ... */ }
std::list<std::unique_ptr<int>> elems;
push_back2(elems, myPtr);            // Error, as you've noted
push_back2(elems, std::move(myPtr)); // Fine, uses move constructor to initialize

Этот синтаксис хорош тем, что заставляет вас явно указать, что вы передаете указатель на функцию.

Как только вы окажетесь внутри push_back2, вы правы, что push_back не сможет принять unique_ptr, потому что попытается использовать несуществующий конструктор копирования. Чтобы это исправить, вам нужно будет снова использовать std::move:

void push_back2(
  std::list<std::unique_ptr<int>>& l, std::unique_ptr<int> a)
{
  l.push_back(std::move(a)); // Fine, moves a
  l.push_back(std::move(a)); // Well that was dumb, but you asked for it!
}

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

3 голосов
/ 22 августа 2011

Ваши знания и предположения о поведении верны.

Пример статьи сбивает с толку, поскольку он объединяет два языка: C ++ с концепциями и C ++ без концепций. На языке притворной работы, list push_back, который требует CopyConstructible, удален SFINAE, оставляя только перегрузку, требующую MoveConstructible. В таком list дизайне, и со старыми правилами, которые lvalue мог связывать с rvalue-ссылкой, тогда push_back неявно двинулся бы с a дважды.

Обратите внимание, что мы не были в опасности в любое время, когда list действительно так себя вел. Авторы просто пытались создать ситуацию, когда const value_type& и value_type&& не были перегружены, а в наборе перегрузки было только value_type&&.

0 голосов
/ 22 августа 2011

Вы можете предоставить его по rvalue - например, по возврату функции.

unique_ptr<int> make_unique() {
    return unique_ptr<int>(new int);
}
int main() {
    list<unique_ptr<int>> lst;
    lst.push_back(make_unique());
}

Кроме того, вы можете явно std::move в список.

...