ссылка на std :: shared: ptr, чтобы избежать подсчета ссылок - PullRequest
1 голос
/ 15 января 2020

Я часто сталкиваюсь с аргументом, что, принимая const std::shared_ptr<T>&, можно избежать приращения счетчика ссылок:

void foo(std::shared_ptr<const int> p); 
void foo2(const std::shared_ptr<const int>& p); 

int main(){
   std::shared_ptr<const int> a = std::make_shared<int>(3);

   foo(a);  // calling this function always does reference counting (atomic locks...)
   foo2(a); // calling this function elides reference counting

   std::shared_ptr<int> b = std::make_shared<int>(3);;
   foo2(b); // what happens here? since there is a cast involved creating a temporary ... (NRVO??)
}

Но я предполагаю, что при вызове foo2(b) подсчет ссылок не исключается? Однако может ли компилятор или std-реализация каким-либо образом подсчитывать ссылки. Будет ли лучше, если будет вызван foo2(std::move(b)), чтобы это исключение произошло?

Ответы [ 2 ]

3 голосов
/ 15 января 2020

Да, перед входом в foo2 обязательно будет увеличение счетчика ссылок и уменьшение при выходе. Это связано с тем, что параметр const std::shared_ptr<const int>& p должен ссылаться на отдельный объект типа std::shared_ptr<const int>, поэтому временный объект должен быть создан и уничтожен.

В пределах языка невозможно исключить этот счетчик ссылок, поскольку если аргумент b изменяется во время выполнения foo2, параметр p должен оставаться неизменным; он должен продолжать указывать на тот же объект int в куче, который может быть изменен, но не удален. (Это не относится к случаям, когда foo2 вызывается с a, так как в этом случае p относится непосредственно к a, а не к временному, поэтому изменения a видны через p.)

Передача std::move(b) в foo2 не будет хорошей идеей, потому что это оставит b пустым и удалит объект int, когда временная привязка к p будет разрушена.

0 голосов
/ 15 января 2020

std::shared_ptr<const int> отличается от типа std::shared_ptr<int>, но существует неявный конструктор преобразования.

Когда вы звоните foo2(b), временный std::shared_ptr<const int> создается из b и связывается с p. Конструктор увеличивает счетчик ссылок, а деструктор уменьшает его.

Когда вы вызываете foo1(a), a копируется в p. p существует на время вызова, а затем уничтожается. Конструктор увеличивает счетчик ссылок, а деструктор уменьшает его.

Когда вы вызываете foo2(a), a привязывается к p. Временные данные не создаются, поэтому счетчик ссылок не изменяется.

Обратите внимание, что в вашем примере нет счетчика ссылок, так как ни 1022 * s, ни const int s не указаны ни одним из указатели.

...