С++ Всегда константная ссылка в конструкторе?

Итак, рассмотрим следующий код:

#include <iostream>
using namespace std;

class A {
public:
    A() = default;

    A(const A& rhs) {
        cout << "Copy was made!" << endl;
    }
};

class B {
public:
    A data;
    int foo;

    B(A data, int foo) : data(data), foo(foo) {
    }
};

int main() {
    A data;

    B foo(data, 10);

    return 0;
}

Это распечатывает:

Копия сделана!

Копия сделана!

Да, это верно, он копирует дважды!

Первая копия происходит, когда мы передаем data конструктору B's. Вторая копия происходит, когда мы копируем data из конструктора в переменную участника.

Мы знаем, что мы не можем пройти под 1 копией (если только мы не пойдем heap & pointers). Так почему бы нам не написать всегда:

B (const A& data, const int& foo, const SomeOtherType& bar, const float& aFloatyNumber) ... и так далее.

Я знаю, что передавать int, float и т. д. по значению дешево. Но, всегда имея const ref в качестве Constructor параметров, мы гарантируем на 1 меньше копий.


person Caresi    schedule 20.09.2015    source источник
comment
we will garantie 1 less copy Это имеет значение, только если копии дорогие. Копирование int дешево - дешевле, чем косвенное обращение, необходимое для доступа к тому же int по ссылке.   -  person Igor Tandetnik    schedule 20.09.2015
comment
и никто не запрещает вам использовать вместо него const A& data. Для POD это не имеет никакого значения, вероятно, компилятор оптимизирует косвенность и просто копирует int и т. д.   -  person vsoftco    schedule 20.09.2015
comment
Потому что семантика, которую вы ищете, - это семантика перемещения (которая поставляется с ссылками на rvalue), а не копии.   -  person gha.st    schedule 20.09.2015
comment
Все в С++ передается по значению под капотом, что-то, унаследованное от С. Поскольку ссылки могут и обычно реализуются с помощью указателей, вам все равно придется копировать базовый указатель для каждого вызова функции. Это будет дороже, чем копирование int, поскольку в конце вы получите косвенное обращение.   -  person StoryTeller - Unslander Monica    schedule 20.09.2015
comment
Мы всегда используем const ref для нетривиальных типов. Если только мы не используем семантику move. И не только для конструкторов. Если только вам не нужна копия.   -  person Galik    schedule 20.09.2015
comment
В Швеции говорят, что маленькие ручейки превращаются в большие реки. Что многие мелочи могут перерасти в большие дела. Разве я не сэкономлю много циклов процессора, помещая const ref в каждый конструктор?   -  person Caresi    schedule 20.09.2015


Ответы (3)


Если вы не перемещаете объекты, которые потребляете, вы фактически должны передавать свои аргументы по ссылкам, возможно, как T const&. Если вы используете свой аргумент, вы должны передать объекты типов с поддержкой перемещения (т. е. типы, определяющие конструктор перемещения) по значению и переместить его. То есть, если A поддерживает перемещение, т. е. имеет конструктор A::A(A&&), вы должны использовать:

B(A data, int foo) : data(std::move(data)), foo(foo) {
}

Если ваши типы не поддерживают перемещение, или вам не нужно выжимать последние биты производительности из построения, или типы предназначены только для перемещения, вы можете безопасно передавать объекты T const&.

person Dietmar Kühl    schedule 20.09.2015

В вашем запросе есть противоречие.

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

Во-вторых, передача объектов по ссылке и примитивов по значениям делается для оптимизации размера.

Если вы хотите передать int как const ref или указатель, это нормально, вы можете это сделать, но получите ли вы что-нибудь из этого.

Если вы хотите скопировать это значение в вызываемой функции в какую-либо другую переменную, то снова будет вызван конструктор.

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

person Praveen Pandey    schedule 20.09.2015

Примитивные типы не копируются, когда в них нет необходимости. Они остаются в стеке или в регистре до конца.

person ypnos    schedule 20.09.2015