3

Consider we need to implement a function f with a templated argument T t. The function should not copy t and accept both rvalues and lvalues, therefore two implementations are possible:

template <class T>
void f(const T& t) { ... }

template <class T>
void f(T&& t) { ... }

If we want to change t inside of f or need to preserve the value category, we have to use the second version. So by this line of thought when and why would we go for the first option?

Seriously
  • 884
  • 1
  • 11
  • 25
  • 3
    *So by this line of thought when and why would we go for the first option?* When you don't need to modify the value? – NathanOliver Dec 16 '19 at 22:08
  • Does this answer your question? [Perfect forwarding - what's it all about?](https://stackoverflow.com/questions/6829241/perfect-forwarding-whats-it-all-about) – Marek R Dec 16 '19 at 22:14
  • @NathanOliver-ReinstateMonica True, but I could still go for the second option. The only advantage I see in version one is explicitly stating that `t` will not change. – Seriously Dec 16 '19 at 22:16
  • @Seriously Which is why you want it. Let the compiler work for you by pointing out your mistakes instead of shipping them. – NathanOliver Dec 16 '19 at 22:17
  • @MarekR Not really. The link seems to only give arguments why to use the second option as far as I see? – Seriously Dec 16 '19 at 22:19
  • Does this answer your question? [Forwarding reference vs const lvalue reference in template code](https://stackoverflow.com/questions/45989511/forwarding-reference-vs-const-lvalue-reference-in-template-code) – Julien Lopez Dec 16 '19 at 22:28
  • @JulienLopez No because OP's code example is not a forwarding reference, hence no one considered this in their answers. – Seriously Dec 16 '19 at 22:33
  • 1
    *"I could still go for the second option"* The first option is more convenient in some cases. E.g. if you want to know the type of the parameter, you can simply write `T` instead of `std::remove_cv_t>`. – HolyBlackCat Dec 16 '19 at 22:47

2 Answers2

8

You'll mainly go for the first option when you want to give strong guarantee to the clients of your function that t won't be changed inside of f. Although you can drop the const qualifier and still not modify t, it's considered good practice and good interface design to qualify as const a parameter if you don't change it's referred to value inside a function, plus it helps the compiler to optimize the code better.

As an extra, know that you can use const_cast to hack around the type safety of const if you really must, but recall that you're doing exactly that: getting rid of the type safety. Avoid this at all cost.

And lastly, a const qualifier doesn't prevent copying, you can easily do something like:

int main() 
{
   const int a = 3;
   int b = a; // a is copied to b
}
Javier Silva Ortíz
  • 2,864
  • 1
  • 12
  • 21
2

In this generic scenario, there is no reason to write both overloads.

If f will only observe its argument, then only the const T& overload is needed.

If f will just forward its argument to some other function, then only the T&& overload is needed, and will use std::forward<T>. (This includes the case where the function needs to retain a copy of the value, i.e., forward it to a copy/move constructor.)

Brian Bi
  • 111,498
  • 10
  • 176
  • 312