why can not add low-level const type to local variable in template function

Viewed 66

I'm studying c++ template, and write a example as shown below:

  #include <vector>
  #include <iostream>
  #include <type_traits>
  #include <typeinfo>

  using namespace std;

  template <typename T>
  void fcn(T &&val)
  {
      cout << is_same<T, int&>::value << endl;
      const T t = val;
      //T const t = val;
      cout << typeid(t).name() << endl;
      t = 10;
      cout << "val: " << val;
      cout << ", t: " << t << endl;
      return;
  }

  int main()
  {
      cout << boolalpha;

      int i{};

      fcn(i);
   }

I expect this code can't compile successfully, but there are no error, the output:

true
i
val: 10, t: 10

I have two questions:

  1. gcc deduce the T is int& type, the line const T t = val, means t const bind to val, like this: const int &t = val, why the const has no effect? t and i value can be changed?

  2. I used typeid(t).name()to show the t's type, why just print i?

3 Answers
  1. In your case, const T with T = int& means, give a reference which I can't reassign. But a reference is never assignable, it always points to the same object. So it collapses to T, which is in that case int&.

To make it works as expected, used std::remove_reference

See : const references in c++ templates

  1. name returns something that is implementation-defined, so there is not much to say about it.
  1. gcc deduce the T is int& type, const T t = val, means t const bind to val, why the const has no effect?

This function template signature

template <typename T> void fcn(T &&val);

uses a forwarding reference. In short, when deducing the instantiation with an lvalue int (your case), you end up with T = int&, i.e., including the reference. Reference collapsing transformes void fcn(T& &&val) to void fcn(T& val).

When you declare const T t, this means T& const (const modifies what's on its left, and if there is nothing on its left, it modifies what's on its right). But this isn't valid as there is no such thing as a const-qualified reference, hence the const-qualifier is dropped.

  1. I used typeid(t).name() to show the t's type, why just print i?

typeid(t).name() isn't required to output a human readable identifier. i stands for int, so depending on the compiler, this is what you should expect. When working with clang or gcc, it is however often nicer to display a type through

template <class T> p(T&&) { std::cout << __PRETTY_FUNCTION__; }

p(t); // prints out understandable type info

I write another program to research const T which using "type alias":

  #include <iostream>                                                                                 

  using namespace std; 

  int main()                                                                                          
  {
    int i {};
    using T = int *;                                                                                
    const T t = &i;     // same with T const t = &i;                                                                                                                                                                                                                                     

    *t = 10;                                                                                                                                                                                                                                                                             
    cout << i << endl;                                                                                                                                                                                                                                                                   
    int j{};                                                                                                                                                                                                                                                                             
    //t = &j;   // error, t is int * const                                                                                                                                                                                                                                                
  } 

it compile successfully, output: 10.

now I can understand why compiler considers const T is int * const, but not const int *, because that compiler treats T as a common type like int, char, not int *, it is not substitution.

Related