When applying const
to a T
that is deduced as an Lvalue reference (for example, when passing an Lvalue to a function that takes universal reference), the modifier is ignored. Suppose we pass a regular Lvalue int
, const T
which should be converted to const int&
is being converted to just int&
.
Here is an example:
#include <iostream>
template<typename T>
const T foo(T&& a)
{
// const T b{ 1 }; This would result in compile-error, cause b is just int&
const T b{ a };
return b;
}
int main()
{
int x = 5;
foo(x);
}
Here is what I got in C++ insights:
#include <iostream>
template<typename T>
const T foo(T && a)
{
const T b = {a} /* NRVO variable */;
return b;
}
/* First instantiated from: insights.cpp:13 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
int & foo<int &>(int & a)
{
int & b = {a}; // As you see I'm getting an int& here and in return type also
return b;
}
#endif
int main()
{
int x = 5;
foo(x);
return 0;
}
You can solve this issue by applying std::remove_reference
to the T
, and then adding the const
and reference, like so:
#include <iostream>
#include <type_traits>
template<typename T>
const std::remove_reference_t<T>& foo(T&& a)
{
const std::remove_reference_t<T>& b{ a };
const std::remove_reference_t<T>& c{ 5 }; // Can use Rvalues now
return b;
}
int main()
{
int x = 5;
foo(x);
}
I was just wondering if this behavior has some explanation? Where else can this behavior rise up? If I'm missing something, I'd appreciate your comments.
It helps to think in east const
terms. Qualifiers like const
associate to the left except when there is nothing to the left - but const T
and T const
actually mean the same thing. const
applies to the whole T
looking at it from the right (east) side.
When T
is int&
then const T
becomes int& const
, which is rather pointless since a reference is immutable anyway.
Had it been pointers, it would become clearer. If T
is int*
then const T
and T const
becomes int* const
which is an immutable pointer, but the object it is pointing at is not const
and can be changed:
using T = int*;
int x = 10;
const T b = &x;
*b = 20; // x is now 20
// b = &x; // error, the pointer is const so you can't assign to it