c++constantsconst-reference

On the weak semantics of references-to-const (and pointers-to-const)


This has probably been already asked.

Why is it allowed to assign a reference-to-const to a non-const variable?

Why is this allowed

int mut {0};
const int & r_to_c {mut};
mut = 1;
// now r_to_c changed to 1!
// But it was supposed to be a reference to something constant!

?

Sure, I cannot mutate the value from the reference-to-const itself. I cannot

r_to_c = 2;

but isn't the const qualification enforcing too little? I would expect, from a promise of const-ness, that binding to mutable variables was disallowed.

Otherwise what guarantees is const giving me? They seem pretty weak, and it seems that this could easily trick programmers to shoot themselves in their foot.

I know that C++ has a reputation for allowing people to shoot themselves in their foot. I don't have a problem with allowing dangerous things. In this case, my problem is that in this case it seems that it is purposefully deceiving, given that the semantics of const here is not the one would expect it.

Mine is a question about the compiler and the language semantics, not about references in particular (I could have asked the same question using a pointer-to-const that is assigned to the address of a non-const variable. Like int mut{0}; const int * p_to_c{&mut};).

Why is the semantics of a reference-to-const (or pointer-to-const) just "you can't use this particular window to modify the thing you see (but if you have other windows that are non-const, you can modify it)" instead of a more powerful "this can only be a window to something that was declared constant and that the compiler guarantees it stays constant"?


[Note on terminology: I use the expression "reference-to-const" instead of "const reference" because a "const reference", interpreted as T& const - consistently with calling T* const a "const pointer" -, does not exist.]


Solution

  • but isn't the const qualification enforcing too little? I would expect, from a promise of const-ness, that binding to mutable variables was disallowed.

    No it is not "too little". You are expecting the wrong thing.

    First, whether you bind a const reference does not make the object itself const. That would be strange:

    void foo(int& x) {
        static const int& y = x;
    }
    

    When I call foo:

    int x = 42;
    foo(x);
    

    I cannot know whether somebody else will keep a const reference to my non-const x.

    Otherwise what guarantees is const giving me?

    You cannot modify something via a const reference:

    void bar(const int& x);
    int x = 0;
    bar(x);
    

    When I call a function that takes a const& then I know that it will not modify my (non-const) parameter. If const references would not bind to non-const objects then there would be no way to make this last example work, i.e. you could pass non-const objects only to functions that do modify them, but not to functions that do not modify them.

    P.S. I can understand your confusion. It is sometimes overlooked that holding a constant reference does not imply that the object cannot be modified. Consider this example:

    #include <cstddef>
    #include <iostream>
    
    struct foo {
        const int& x;
    };
    
    int main() {
        int y = 0;
        foo f{x};
        std::cout << f.x;   // prints 0
        y = 42;
        std::cout << f.x;   // prints 42
    }
    

    Printing the value of the member to the screen yields two different results, even though foo::x is a constant reference! It is a "constant reference" not a "reference to a constant". What const actually means here: You cannot modify y through f.x.