Because you have two levels of indirection - in your main function, that call to value
returns a reference to a const pointer to a non-const foo
.
This can safely be copied into non-const pointer to a non-const foo
.
If you'd instantiated test
with const foo *
, it would be a different story.
const test<const foo*> t;
foo* f = t.value(); // error
const foo* f = t.value(); // fine
return 0;
Update
From the comment:
value() returns const T& which can
only be assigned to another const
type. But in this case, compiler is
safely allowing the conversion.
Const data can only be read. It cannot be written ("mutated"). But copying some data is a way of reading it, so it's okay. For example:
const int c = 5;
int n = c;
Here, I had some const data in c
, and I copied the data into a non-const variable n. That's fine, it's just reading the data. The value in c
has not been modified.
Now, suppose your foo
had some data in it:
struct foo { int n; };
If I have a non-const pointer to one of those, I can modify the n
value through the pointer. You asked your test
template to store a pointer to a non-const foo
, and then made a const instance of test
. Only the pointer address is constant, therefore. No one can change the address stored in the pointer inside test
, so it cannot be made to point to another object. However, the object it points to can have its contents modified.
Update 2:
When you made your non-template version of the example, you made a mistake. To get it right, you need to substitute foo *
into each place where there's a T
.
const T& value() const
Notice that you have a reference to a const T
there. So the return value will be a reference to something const: a foo *
. It's only the pointer address that can't be modified. The object it points to can have its contents modified.
In your second example, you got rid of the reference part, which changes the meaning and makes the const
modifier apply to the object that the pointer points to, instead of applying to the pointer itself.