Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

is_const doesn't work as expected for reference

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?

like image 913
vik santata Avatar asked May 13 '16 06:05

vik santata


3 Answers

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
like image 99
songyuanyao Avatar answered Oct 19 '22 10:10

songyuanyao


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.

like image 30
xaxxon Avatar answered Oct 19 '22 10:10

xaxxon


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:

  1. T& & becomes T&
  2. T& && becomes T&
  3. T&& & becomes T&
  4. T&& && becomes T&&

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.

enter image description here

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.

like image 33
101010 Avatar answered Oct 19 '22 10:10

101010