Template type deduction of const reference type

587 Views Asked by At

It is a bit confusing to me about how C++ 11 does template deduction when const references to a template parameter types are used. Consider the following program:

template <typename T> void test_func(const T &a){
    (*a)++; 
}

int main() {
    // 1st case
    int i = 1;
    test_func(&i); 
    
    // 2nd case
    const int* cPtr = &i; 
    test_func(cPtr); 
}

My questions are:

  • For the first case, it is compiled fine; so it seems like the instantiated template function parameter is int* const &a (a top-level const); but if we directly replace T with int*, we get const int* &a` (a low-level const) and compiler should have failed; how can we express a low-level const in a format of "const T &a"; I am confused what is the real type of the parameter and what is the type of T;
  • For the second case, compilation fails with an
    error: increment of read-only location `*(const int*)a;'
    
    It seems like the second instantiated function inherits the low-level const as well; then what is the type of parameter; what is the type of T;
2

There are 2 best solutions below

0
On

Your test_func() receives a const reference to something. In the first case, the something is the address of an integer, so the final type is "a reference which cannot be used to change its referent which is a pointer to an integer." This means you can use the pointer to the integer, including using it to change the integer, but you cannot change the value of the pointer (i.e. the address) to point to something else.

In the second case the final type is "a reference which cannot be used to change its referent which is a pointer to an integer and the pointer cannot be used to change the integer." Therefore, *a is const int which cannot be modified.

7
On

First case

if we directly replace T with int*, we get const int* &a (a low-level const) and the compiler should have failed.

Not really...

The thing is, C++ (following C) has a somewhat convoluted syntax for declaring variables, "directly-replacing" doesn't mean "simple copy-pasting". In your case, let's suppose you rewrite your declaration as T const&. Now if you copy-paste you get:

void test2(int * const &a){
    (*a)++; 
}

which should compile just fine, and does.

Second case

what is the type of T?

Let's check! Following an answer to this question: Print template typename at compile time

we write:

template <typename T> void test_func(T const &a){
    bool x = T::nothing; 
    (*a)++; 
}

int main() {
    int i = 1;
    // 2nd case
    const int* cPtr = &i; 
    test_func(cPtr);     
}

and when we run this program, we get:

source>: In instantiation of 'void test_func(const T&) [with T = const int*]':
<source>:10:19:   required from here
<source>:2:17: error: 'nothing' is not a member of 'const int*'
    2 |     bool x = T::nothing;
      |                 ^~~~~~~

so, T is const int* for this case.