Among the many benefits of const qualification is to make an API more understandable, example:
template int function1(T const& in);
// clea
You could say this:
template <typename T>
typename std::enable_if<immutable<T>::value, int>::type
function(T && in)
{
// ...
}
where you have something like:
template <typename T> struct immutable
: std::integral_constant<bool, !std::is_reference<T>::value> {};
template <typename U> struct immutable<U const &>
: std::true_type {};
This way, the template will only be usable if the universal reference is either a const-reference (so T = U const &
) or an rvalue-reference (so T
is not a reference).
That said, if the argument is not going to be changed, you could just use T const &
and be done with it, since there's nothing to be gained from binding mutably to temporary values.
template<typename T> int function2(T&& in); // can explicitly forward the input if it's an rvalue
Apart from documentation, is there a good way to describe that function2 won’t change its input?
Yes. Stick with the C++03 solution:
template<typename T> int function1(T const& in);
// clearly, the input won’t change through function1
The benefits of perfect forwarding are that you don't want to assume if something is const
or non-const
, lvalue or rvalue. If you want to enforce that something is not modified (i.e. that it is const
), then explicitly say so by adding const
.
You could do this:
template<typename T> int function1(T const&& in);
// clearly, the input won’t change through function1
However everyone who read your code would wonder why you've used rvalue references. And function1
would cease to accept lvalues. Just use const &
instead and everyone will understand. It is a simple and well understood idiom.
You don't want to perfectly forward. You want to enforce immutability.