2015-11-05 2 views
4

Прошу простить мое отсутствие ясности по этой теме. Я пытаюсь создать функции для вставки большого класса в вектор. В этом примере я использую вектор ints как большой класс.Именованные ссылки на ссылки

#include <vector> 
#include <iostream> 
using namespace std; 

vector<vector<int>> vectorOfVectors; 

void fn(const vector<int> &v) { 
    vectorOfVectors.push_back(v); 

} 
void fn(vector<int> &&v) { 
    vectorOfVectors.push_back(std::move(v)); 
} 

int main() { 
    vector<int> a({1}); 
    const vector<int> b({2}); 
    fn(std::move(a)); 
    fn(b); 
    cout<<b[0]; 
} 

Очевидно, что не нужно копировать, когда это возможно. Мои вопросы:

  1. Имеет ли этот код правильную вещь?
  2. Есть ли лучший способ сделать это?
  3. Для того же подхода к работе с пользовательскими классами, нужно ли определять конструкторы перемещения?

ответ

7

Имеет ли этот код правильную вещь?

Да. C + + 11 добавил std::vector::push_back(T&&) именно по этой причине.

Есть ли лучший способ сделать это?

Ваш fn(const vector<int> &v) и fn(vector<int> &&v) оба делают то же самое, выдвигая аргумент v на конец vectorOfVectors. Вместо ваших двух функций fn вы могли бы использовать один шаблон функции, который использует совершенную пересылку.

template<typename T> 
void fn(T &&v) { 
    vectorOfVectors.push_back(std::forward<T>(v)); 
} 

Как это работает благодаря 11 reference collapsing rules и std::forward С ++. Тип шаблона T становится vector<int>& в том случае, если v является lvalue, но vector<int>&& в том случае, если v является rvalue. Правила обхода ссылок означают, что vector<int>& && становится vector<int>&, а vector<int>&& && становится vector<int>&&. Это делает именно то, что вы хотите, вызывая версию push_back, которая делает копию в случае lvalue, но версия, которая выполняет движение в случае rvalue.

Одна из недостатков заключается в том, что иногда это может привести к интересной диагностике, когда вы ошиблись. («Интересно» здесь означает сотни строк непостижимого диагностического текста из g ++ или clang ++). Другой недостаток заключается в том, что шаблоны могут привести к тому, что «конвертеры ушли в дикую природу».

Для того же подхода к работе с пользовательскими классами необходимо определить конструкторы перемещения?

Не обязательно. Вы получите неявно объявленный конструктор перемещения, если класс не объявляет определяемый пользователем деструктор, конструктор копирования, оператор присваивания копии или оператор переадресации. Неявно объявленный конструктор перемещения будет определен как удаленный, если класс не имеет подвижных членов данных или получен из класса, который нельзя перемещать или удалять.

Для меня это слишком много, чтобы помнить. Я не знаю, является ли это хорошей практикой или плохим, но я начал использовать Foo(const Foo&)=default, с аналогичными объявлениями для другого правила пяти функций. Я также квалифицирую конструкторы как explicit во многих случаях, чтобы избежать проблем с «конвертерами».

+0

Не могли бы вы рассказать о части «В некоторых случаях да»? И как работает шаблон? – SPMP

+0

@ user2308211 - Я изменил «в некоторых случаях, да» на ваш конкретный случай. Обобщая немного, вы должны подумать о templatizing функции, если у вас есть 'f (T &)' и 'f (T &&)', которые являются копиями и вставками, идентичными друг другу, за исключением 'std :: move' или двух в версии перемещения 'f()'. –

+2

«Тип шаблона' T' становится [...] 'vector &&' в случае, если 'v' является rvalue" - нет, в этом случае 'T' является просто' vector '. Справочная версия пересылки также улавливает все под солнцем (важно, если у вас есть перегрузки или нужно проверить, правильно ли форматируется 'f (что-то)'). Наконец, если проблема с типом дешево перемещаться (например, 'vector '), более простой подход - принять аргумент по значению, а затем безоговорочно переместить: 'void f (vector v) {vectorOfVectors.push_back (std :: шаг (v)); } '. –

2
  1. Во избежание копирования a.
  2. Да. Использование push_back означает, что вы вынуждены строить не менее двух объектов, в то время как emplace_back, и идеальная пересылка может сделать меньше работы.

    template<typename... Ts> 
    auto fn(Ts &&...ts) 
        -> decltype(vectorOfVectors.emplace_back(std::forward<Ts>(ts)...), void()) 
    { 
        vectorOfVectors.emplace_back(std::forward<Ts>(ts)...); 
    } 
    

    http://melpon.org/wandbox/permlink/sT65g3sDxHI0ZZhZ

3. До тех пор, пока вы используете push_back, вы должны были бы классы, которые будут двигаться конструктивны, чтобы избежать копирования. Вы не обязательно должны сами определять конструктор перемещения, если вы можете получить определение по умолчанию.

Смежные вопросы