I wrote a test program:
#include <iostream>
#include <type_traits>
using namespace std;
template<class T>
void f(T&& t)
{
cout<<is_const<T>()<<endl;
//++t;
}
int main() {
const int i=0;
f(i);
return 0;
}
It outputs "0", showing T
is not const! This is weird. Then I modified f
:
template<class T>
void f(T&& t)
{
cout<<is_const<T>()<<endl;
++t;
}
Then there's compiler error, saying we're modifying a read only t
.
So is t
modifiable or not, at all? Is there any mis-assumption in my program?
See std::is_const:
If
T
is a const-qualified type (that is, const, or const volatile), provides the member constant value equal true. For any other type, value is false.
t
is declared as a forwarding references. So for your code, T
will be deduced as const int&
, which is a reference. Reference can't be const-qualified, it won't be const itself. Precisely, there's not const reference (i.e. int& const
), because reference couldn't be rebound again. const int&
is a reference to const int
; and note that t
is thus not modifiable.
From the standard, $8.3.2/1 References [dcl.ref]
Cv-qualified references are ill-formed except when the cv-qualifiers are introduced through the use of a typedef-name ([dcl.typedef], [temp.param]) or decltype-specifier ([dcl.type.simple]), in which case the cv-qualifiers are ignored.
More examples from cppreference:
std::cout << std::is_const<int>::value << '\n'; // false
std::cout << std::is_const<const int>::value << '\n'; // true
std::cout << std::is_const<const int*>::value << '\n'; // false
std::cout << std::is_const<int* const>::value << '\n'; // true
std::cout << std::is_const<const int&>::value << '\n'; // false
Whether t
is modifiable is dependent on the type of T
which is deduced based on the type of variable passed in. In this case you're passing in a const int
, so t
is of type const int &
because you're accepting it as a forwarding reference.
As far as why is_const
is returning false, that's because T
a reference type and references are never const.
Your template function (i.e., f
) takes as a parameter a forwarding reference (a.k.a universal reference). The rules that determine the deduction of T
are refereed as reference collapsing rules. These rules are summarized bellow:
T&
&
becomesT&
T&
&&
becomesT&
T&&
&
becomesT&
T&&
&&
becomesT&&
Now, according to the reference collapsing rules, when you supply as parameter to f
int const i
, T
will be deducted to int const&
.
According to the C++ standard table 52 is_const
would evaluate to true
if T
is const
qualified.
Furthermore, in the C++ standard §20.13.4.3/p5 Type properties [meta.unary.prop] there's the following example of how is_const
type trait works:
[Example:
is_const<const volatile int>::value // true is_const<const int*>::value // false is_const<const int&>::value // false is_const<int[3]>::value // false is_const<const int[3]>::value // true
— end example ]
As you can see at the third line which is our case is_const
evaluates to false
. Why? Because the type passed in to is_const
as a template parameter is a reference type. Now, references are inherently const
in the sense that you can't change what they refer to, but they are not const
qualified. Thus, is_const
with a reference type will evaluate to false
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With