Является ли передача по значению разумным значением по умолчанию в C ++ 11? - PullRequest
135 голосов
/ 29 сентября 2011

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

Теперь в C ++ 11 у нас есть ссылки на Rvalue и конструкторы перемещения, что означает, что можно реализовать большой объект (например, std::vector), который дешево передавать по значению в функцию и из нее.

Итак, означает ли это, что по умолчанию должна передаваться по значению для экземпляров таких типов, как std::vector и std::string? Как насчет пользовательских объектов? Какая новая лучшая практика?

Ответы [ 3 ]

135 голосов
/ 29 сентября 2011

Это разумное значение по умолчанию , если , вам нужно сделать копию внутри тела. Это то, что Дейв Абрахамс защищает :

Рекомендация: не копируйте аргументы вашей функции. Вместо этого передайте их по значению и позвольте компилятору выполнить копирование.

В коде это означает, что не делайте этого:

void foo(T const& t)
{
    auto copy = t;
    // ...
}

но сделайте это:

void foo(T t)
{
    // ...
}

, преимущество которого в том, что вызывающий абонент может использовать foo примерно так:

T lval;
foo(lval); // copy from lvalue
foo(T {}); // (potential) move from prvalue
foo(std::move(lval)); // (potential) move from xvalue

и только минимальная работа сделана. Вам потребуется две перегрузки, чтобы сделать то же самое со ссылками, void foo(T const&); и void foo(T&&);.

Имея это в виду, я теперь написал свои ценные конструкторы так:

class T {
    U u;
    V v;
public:
    T(U u, V v)
        : u(std::move(u))
        , v(std::move(v))
    {}
};

В противном случае передача по ссылке на const все еще является разумной.

71 голосов
/ 29 сентября 2011

Почти во всех случаях ваша семантика должна быть либо:

bar(foo f); // want to obtain a copy of f
bar(const foo& f); // want to read f
bar(foo& f); // want to modify f

Все остальные подписи должны использоваться только экономно и с достаточным основанием. Компилятор теперь всегда будет работать наиболее эффективным способом. Вы можете просто написать свой код!

10 голосов
/ 20 декабря 2012

Передайте параметры по значению, если внутри тела функции вам нужна копия объекта или вам нужно только переместить объект.Пройдите мимо const&, если вам нужен только доступ без мутаций к объекту.

Пример копирования объекта:

void copy_antipattern(T const& t) { // (Don't do this.)
    auto copy = t;
    t.some_mutating_function();
}

void copy_pattern(T t) { // (Do this instead.)
    t.some_mutating_function();
}

Пример перемещения объекта:

std::vector<T> v; 

void move_antipattern(T const& t) {
    v.push_back(t); 
}

void move_pattern(T t) {
    v.push_back(std::move(t)); 
}

НеПример с изменяющимся доступом:

void read_pattern(T const& t) {
    t.some_const_function();
}

Обоснование см. в этих сообщениях блога Дейва Абрахамса и Сян Фана .

...