2

In Herb Sutter's 2014 CppCon talk, he talks about how you shouldn't have smart pointers in your function declaration if you don't intend to transfer or share ownership. And most definitely, no const references to smart pointers.

If I have a function which accepts an element that a smart pointer points to, that's pretty easy to implement. You just do:

void f(int& i) //or int*
{
  i++;
}

int main()
{
  auto numberPtr = std::make_unique<int>(42);
  f(*numberPtr);
}

But what I was wondering, is there a best practice for range-based loops?

int main()
{
  auto numberPtrVec = std::vector<std::unique_ptr<int>>{};
  
  for(int i = 0; i < 5; i++)
    numberPtrVec.push_back(std::make_unique<int>(i));

  for(auto& i : numberPtrVec)
  {
    //i++; would be the optimum
    (*i)++; //dereferencing is necessary because i is still a reference to an unique_ptr
  }
}

Is there any way to directly capture the current element as a reference or a raw-pointer?

For me, it always feels a little bit wrong to handle references to smart pointers.

5
  • 1
    Why? I just see no good reason. If you don't want the ownership to be transferred, just use auto const& to ensure that. Unlike function declarations, there is no detriment. Commented Apr 11, 2023 at 11:01
  • Because smart pointers should only be used to express ownership. If you see a smart pointer there should be a reason why it's there. Code littered with references to smart pointers are hard to understand and always slower because of the extra level of redirection necessary. Commented Apr 11, 2023 at 11:48
  • I don't see how calling -> or * or .get() on a smart pointer instead of a regular pointer would ever slow the code, as it would be inlined. Nor do I see a person failing to understand a code where a smart pointer is used inside a for-loop in a trivial way. It's like you repeat words without understanding them where they apply. Commented Apr 11, 2023 at 16:42
  • The original author of the codebase I'm working on didn't understand smart pointers and used and passed them as references literally everywhere. To the point where you couldn't be sure if an object was the owner of a pointer or not (Doesn't matter right? They get deleted anyway...). During refactoring i stumbled over these loops and wondered if I can apply the same rules to them. Of course you can just loop over the pointers and dereference them. But my initial intent is to loop over the objects inside. If there is no canonic way that's fine. But that doesn't make my question invalid. Commented Apr 12, 2023 at 6:44
  • unlike functions/objects there isn't any particular benefit in dereferencing the smart pointer inside the for-loop nor there are issues in using a reference to a smart pointer in such a scenario. Functions/objects is a different matter. Commented Apr 12, 2023 at 7:35

2 Answers 2

6

Just grab a reference as the first order of business in the for-loop:

for(auto& i : numberPtrVec)
{
    auto &n = *i;

and then work with n in the rest of the loop.

Sign up to request clarification or add additional context in comments.

Comments

3

In C++20, I'd suggest using std::ranges::transform(deref), where deref is an operator wrapper in the style of std::plus.

For C++17, you'd need a ranges library for that, such as range-v3's views::indirect:

#include <range/v3/view/indirect.hpp>

for(auto& i : numberPtrVec | ranges::views::indirect)
{
  i++;
}

1 Comment

Looking at C++20 this might be the most elegant way. Hopefully in a not so distant future the safest and most expressive code will also be the default.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.