-1

Since a reference (some-type&) behaves as a constant pointer (some-type * const), passing an argument via a "constant reference to pointer" parameter (some-type * const&) seems a bit redundant, because

  • if the parameter is some-type *
    • I'm copying it,
    • I can't edit the original because I just don't handle it;
  • if the parameter is some-type * const&
    • I'm not copying it,
    • I can't edit the orignal because I hold it as a const entity.

So in some way the only difference between the two is that the former incurs a copy, whereas the latter doesn't.

In turn, this means that this difference can be relatively important for std::shared_ptrs, which can be bigger than raw pointers and std::unique_ptrs.

But is that (passing std::shared_ptrs by const& if they are "heavy") the only usecase for passing pointer arguments by const&?


As regards the observation that the pointer aspect of the question is unnecessary, I think that when talking of non pointer-like data types, it's kind of easy to see the biggest advantage (or one of the biggest advantages) of passing them by value vs by const&: in both cases you protect the actual entity at the call site, but in the latter you avoid copying a potentially huge object.

For pointer-like entities such as raw pointers and smart pointers, I look at them as things which are meant to be at least similar in size to each other, with the std::shared_ptr allowed to diverge from the similarity.

Anyway, if we are dealing with obviously small raw pointers and std::unique_ptrs and small std::shared_ptrs, so that we can consider the aforementioned advantage of passing by const& negligible, is there any other reason why I might want to pass them by const&?

Enlico
  • 23,259
  • 6
  • 48
  • 102
  • A reference may not necessarily be implemented as a constant pointer under the hood. Some compilers *may* use that, as an implementation detail, but that self same compiler may also use other mechanisms that do not rely on that approach. – Eljay Jul 14 '22 at 14:10
  • 1
    Pointers and smart pointers are different things. Which are you asking about? – Passer By Jul 14 '22 at 14:10
  • That first sentence is really triggering – StoryTeller - Unslander Monica Jul 14 '22 at 14:11
  • not limit to pointer, your question may also apply to `int` and `const int&`. – apple apple Jul 14 '22 at 14:12
  • TLDR No references are not pointers. in fact you should first look at STL datatypes (containers), then at non-raw pointers like std::unique_ptr (for manually allocated memory or polymorphism use case). Only if you are very sure of the lifetime of an (optional or polymorphic object) use raw pointers. Maybe you should read : https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines and lookup anything pointer. – Pepijn Kramer Jul 14 '22 at 14:13
  • @Eljay, is it better now? – Enlico Jul 14 '22 at 14:14
  • @PasserBy, both of them. – Enlico Jul 14 '22 at 14:15
  • You can't just say "both of them" when the second half of the question needs to follow from the first, but it doesn't because they're unrelated. – Passer By Jul 14 '22 at 14:22
  • I concur with *apple apple*. The **pointer** aspect of the question just muddies the waters. It could be asked with `int` parameter versus `const int&` parameter. I surmise that C++ provides general purpose mechanisms that do not need to be utilized for certain situations, but the language does not forbid (mis-/ab-)using them in those situations. And from *463035818_is_not_a_number*'s answer, perhaps there may be a real-world scenario where it is important to distinguish (although I'm hard pressed to think of one at the moment; I don't think I've ever needed to do so). – Eljay Jul 14 '22 at 14:50
  • @Eljay, I've tried to clarify why I specified the pointer aspect of the question. – Enlico Jul 14 '22 at 16:31
  • @Enlico `T*` and `std::shared_ptr` are pretty different. moreover you cannot even pass `std::unique_ptr` by value without move it out. please be specific what you want to ask. – apple apple Jul 14 '22 at 17:23
  • oh and maybe `T*` would larger than `int`, but not much anyway. – apple apple Jul 14 '22 at 17:25
  • to some point I start suspect you're trying to ask `const T*` vs `T*const` ... – apple apple Jul 14 '22 at 17:26
  • 1) _without move it out_, yes, so you can pass it by value; 2) no, it's pretty clear to me what constant pointer and a constant pointee are. – Enlico Jul 14 '22 at 17:40
  • @Enlico 1) so all 3 are **different situation** (please be specific what you want to ask) 2) well then can you clarify what the difference between `int` and `T*` (or `uintptr_t`) here? – apple apple Jul 14 '22 at 17:58
  • Look, I don't understand your point. One more way I can clarify my question is: do you agree that pointers (smart or not) can be passed by value? (That in the case of `unique_ptr` this forces the client to move is a detail; important as you want, but doesn't change that the parameter is a value type.) I'm asking if it makes sense to pass by `const&` instead. Two answers have been given already. Unless they are motivated by _"your question makes no sense, so I post an answer to another question I can think of"_, I guess my question does make sense. Happy to improve it, if you help me doing it. – Enlico Jul 15 '22 at 05:30
  • As regards the fact that the question _do you agree that pointers_ can be applied to non pointers, yes, I know. But I'm not interested in the criteria of the choice between passing by value or `const&` for non pointer-like objects, because that seems clear to me. – Enlico Jul 15 '22 at 05:31
  • well you're confusing classes (including smart pointer) and `T*`. – apple apple Jul 15 '22 at 13:42
  • re: "do you agree that pointers can be applied to non pointers", I never say that. I said compare `const T&` and `T` is the same when `T=int` and `T=U*` – apple apple Jul 15 '22 at 13:45
  • @appleapple am I confusing classes and pointers because I'm referring to raw pointers and smart pointers together by the word pointers? Fwiw, we can even say "generalized pointers". I'm just caring about the fact that all of them are meant to "point to something". Anyway, I'm sorry, I have to accept that I'm stupid and that I don't understand your point. If you feel an answer would help, feel free to post it. – Enlico Jul 15 '22 at 16:21
  • @Enlico smart pointer are *class*. they are what you call *potentially huge object*. just by accident it refers to single element doesn't change it. – apple apple Jul 15 '22 at 17:03
  • @Enlico so let me add a conclusion. – apple apple Jul 15 '22 at 17:18
  • 1. you should ask one question per post. it's also a stackoverflow rule, for good reason. Mixing classes and raw pointer is no help for you to understand. (why not add other containers all together then?) – apple apple Jul 15 '22 at 17:20
  • 2. for some reason you're insisting `T*const&` vs `T*` is pretty different from `const uintptr_t&` vs `uintptr_t` without ever state a reason. (possibly also because you don't realize smart pointer is pretty different from raw pointers) – apple apple Jul 15 '22 at 17:23
  • BTW, re: " Two answers have been given already." : well one is not in your form and another just shows you ask multiple unrelated question at same time. I'm not sure it support any of your point. – apple apple Jul 15 '22 at 17:26

2 Answers2

2

Without a reference you cannot overload a function based on constness of the parameter:

void foo(int* const) { /* do something */ }
void foo(int*) { /* do something else */ }  // error : redefinition of foo

But you can do so with a const/non-const reference:

void bar(int*&) { std::cout << "non-const\n";}
void bar(int* const&) { std::cout << "cosnt\n";}

int main() {
    int* y = nullptr;
    int* const x = nullptr;
    bar(y);
    bar(x);
}
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • @PasserBy with `bar(int*)` and `bar(int*&)` the call `bar(y)` is ambiguous – 463035818_is_not_an_ai Jul 14 '22 at 14:27
  • @PasserBy its not the two signatures in the question because I am trying to make a point in favor of `some-type * const&`. Maybe I am missing something, but I don't see how to overload the function without using the reference – 463035818_is_not_an_ai Jul 14 '22 at 14:29
  • 1
    why would you do this? – user253751 Jul 14 '22 at 15:06
  • @user253751 That's a very good question. – Paul Sanders Jul 14 '22 at 16:22
  • You should always use the `const` version, unless you intend to modify the local variable. But instead of the `const` version just use `int&`. Same thing but easier to use. The non-const version makes seldom sense if you follow the C++ Core Guidelines. A raw pointer should always point to a single element so modifying it other than full replacement (e.g. `node = node->next`) make no sense. – Goswin von Brederlow Jul 14 '22 at 23:32
2

To answer the title of your question, for raw pointers, no. Passing a pointer by reference is usually more expensive, and if it's a const reference you can't use it to modify the caller's pointer anyway so there's no benefit (and I really don't know what @463035818_is_not_a_number is getting at, who would want ever such overloads?)

For a smart pointer, it's different. std::unique_ptr cannot be passed by value at all (since the copy constructor is deleted) so you can either pass by reference (if you just want to get at what it's pointing to) or you can move from it (to transfer ownership). And in fact, there's not really much point in passing it by const reference since you can pass my_unique_ptr.get() as a raw pointer - by value - and not lose anything.

std::shared_ptr can be passed by value, but doing that will bump the reference count (and then decrement it again when the copy goes out of scope), and there is a cost associated with that so you might want to pass it by const reference since that may well be cheaper. But, again, if the function you're calling is non-owning, why not just pass a raw pointer? This is the cheapest option of all.

The only reason to copy a shared_ptr is to make sure that the pointer it is managing does not disappear out from beneath you, and that implies you're hanging on to it for some reason (so-called 'shared ownership'). So yes, if that's the semantics you want, pass it by const reference and make a copy at the receiving end.

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48