2

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;
einpoklum
  • 118,144
  • 57
  • 340
  • 684
Marcus_Ma
  • 155
  • 1
  • 1
  • 6
  • `const T &a` is clearly "a const reference to T". Wouldn't it be very strange and surprising if passing `const int*` as `T` and suddenly you would get "a non-const reference to T where T is `const int*`"? I would find that very counter-intuitive. – super Jul 26 '20 at 07:59

2 Answers2

4

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.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
2

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.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • is `T const &` preferred in declaration than `const T &`? I understand it is low-level const in the format of 'T const &'; – Marcus_Ma Jul 26 '20 at 19:00
  • No, neither is preferred. Or rather - it's a "religious" debate... see also: [Const before or const after?](https://stackoverflow.com/q/5503352/1593077). As for "low-level const" - there is no such term. That is, I've never heard of it. The question is only what construct the const applies to. The C grammar is a bit confusing with its rule; and you can't always clarify with (parentheses), since those are also used to declare a function. – einpoklum Jul 26 '20 at 19:18
  • Umm, I quoted "low-level const" from C++ primer, see this: http://www.cplusplus.com/forum/beginner/143366/; now, my problem is more about the format; this may not make sense at all; but my question is: if in the template, we declare parameter in the format of `const T &`, how it can become `int* const &`? Those two forms does not match. – Marcus_Ma Jul 26 '20 at 19:50
  • 1
    Just don't think of template instantiation as purely copy-pasing of text. The template type is "A reference to a constant T". If T is "a pointer to int", then the instantiated template is "A reference to a constant pointer-to-int". As though the T is "boxed". – einpoklum Jul 26 '20 at 20:10
  • @Marcus_Ma: Ok, I take back my earlier comment. Apparently one of these terms was officially introduced into the standard for C++17. They're still rarely used in my experience. – einpoklum Jul 26 '20 at 20:29
  • got it! Thanks very much for your explanation! – Marcus_Ma Jul 26 '20 at 23:15
  • @Marcus_Ma: On Stackoverflow, the way to thank people is by accepting their answer or upvoting - that's all the thanks we need. – einpoklum Jul 27 '20 at 06:48