Что означает T&& (двойной амперсанд) в C++11?
Я изучаю новые возможности C++11, и одна из них, которую я заметил, — это двойной амперсанд при объявлении переменных, например, T&& var
.
Во-первых, как называется эта конструкция? Мне бы хотелось, чтобы Google позволял искать подобные символы.
Что именно это означает?
На первый взгляд, кажется, что это двойная ссылка (похожая на двойные указатели в C — T** var
), но я затрудняюсь найти практический пример её использования.
1 ответ(ов)
Это обозначает ссылку на rvalue (временный объект). Ссылки на rvalue привязываются только к временным объектам, если не задано иное. Они используются для повышения эффективности работы с объектами в определённых условиях и предоставляют механизм, известный как идеальная передача (perfect forwarding), что значительно упрощает шаблонный код.
В C++03 вы не можете отличить копию немутируемого lvalue от rvalue.
std::string s;
std::string another(s); // вызывает std::string(const std::string&);
std::string more(std::string(s)); // вызывает std::string(const std::string&);
В C++0X это уже не так.
std::string s;
std::string another(s); // вызывает std::string(const std::string&);
std::string more(std::string(s)); // вызывает std::string(std::string&&);
Рассмотрим реализацию этих конструкторов. В первом случае строка должна выполнить копирование для поддержания семантики значений, что подразумевает выделение новой памяти в куче. Однако во втором случае мы заранее знаем, что объект, переданный в конструктор, вскоре будет уничтожен, и его не нужно оставлять неизменным. Мы можем эффективно просто поменять внутренние указатели и избежать копирования в данном сценарии, что значительно более эффективно. Семантика перемещения выгодна для любого класса, для которого дорого или невозможно копирование внутренних ресурсов. Возьмём, к примеру, std::unique_ptr
— теперь, когда наш класс может различать временные и нетемпоральные объекты, мы можем корректно реализовать семантику перемещения, так что unique_ptr
нельзя копировать, но его можно перемещать, а это значит, что std::unique_ptr
можно законно хранить в стандартных контейнерах, сортировать и так далее, в то время как std::auto_ptr
из C++03 не позволяет этого.
Теперь рассмотрим другое применение ссылок на rvalue — идеальную передачу. Рассмотрим вопрос привязывания ссылки к ссылке.
std::string s;
std::string& ref = s;
(std::string&)& anotherref = ref; // обычно выражается через шаблон
Не помню, что говорит C03 об этом, но в C0x результатирующий тип при работе с ссылками на rvalue имеет критическое значение. Ссылка на rvalue типа T, где T — это тип ссылки, становится ссылкой типа T.
(std::string&)&& ref // ref — это std::string&
(const std::string&)&& ref // ref — это const std::string&
(std::string&&)&& ref // ref — это std::string&&
(const std::string&&)&& ref // ref — это const std::string&&
Рассмотрим самую простую функцию-шаблон — min и max. В C03 вы должны вручную перегрузить для всех четырёх комбинаций const и не-const. В C0x достаточно одной перегрузки. В сочетании с вариативными шаблонами это позволяет реализовать идеальную передачу.
template<typename A, typename B> auto min(A&& aref, B&& bref) {
// например, если вы передадите const std::string& в качестве первого аргумента,
// тогда A станет const std::string&, и, следовательно, aref также станет
// const std::string&, полностью сохраняя информацию о типе.
if (std::forward<A>(aref) < std::forward<B>(bref))
return std::forward<A>(aref);
else
return std::forward<B>(bref);
}
Я оставил без внимания вывод типа возвращаемого значения, потому что не припоминаю, как это делается наизусть, но этот min может принимать любую комбинацию lvalues, rvalues и const lvalues.
Что такое лямбда-выражение и когда его следует использовать?
Почему следует использовать указатель вместо самого объекта?
Что такое std::move() и когда его следует использовать?
Что такое Правило трёх?
Какова разница между 'typedef' и 'using'?