1

I have the following classes declaration, and according to what I've learned related to const member functions, const objects can not call non-const member functions. In the range-for loop, we are using "const auto animal", which is supposely using a const object, so I think that the const object should give a compile error when calling the non-const member function speak(), but it actually compiles, why?, maybe I dont have a clear idea of how the range-for loop really works... Thanks!

#include <iostream>
#include <string>

class Animal {
protected:
     std::string name_;
     std::string speak_;
public:
    Animal(const std::string &name, const std::string &speak) : name_(name), speak_(speak){}
    const std::string &getName() const  { return name_;}
    std::string speak()  { return speak_;}
};

class Cat : public Animal{
public:
 Cat(const std::string &name) : Animal(name, "meow"){}
};

class Dog : public Animal{
public:
 Dog( const std::string &name) : Animal(name, "woof"){}
};

int main() {
    Cat tom{ "tom" };
    Dog felix{ "felix" };

    Animal *animals[]{ &tom, &felix};
     for (const auto &animal : animals)
         std::cout << animal->getName() << " says " << animal->speak() << '\n';


    return 0;
}
learning_dude
  • 1,030
  • 1
  • 5
  • 10
  • All the magic is under `auto`. if you try to open in you can see that this is a reference to constant pointer but not the reference to pointer to constant. That's why you can call non const methods of `Animal` – vsh Dec 23 '19 at 22:35

1 Answers1

6

The const auto& here becomes a const reference to a variable of type Animal*. This means you can't change where the pointer points to, but the pointed-to value itself is still mutable.

Replacing auto would look like:

for (Animal* const& animal : animals)
  // ...
N. Shead
  • 3,828
  • 1
  • 16
  • 22
  • Hi! thanks, this answers my question, but it raises another one...How can you be shure, or how do you know that this is the form that "auto" is taking (besides the fact that it works this way). I mean, I could think as well that auto is taking for(const Animal *animal)...This is actually the reason why I posted this question – learning_dude Dec 23 '19 at 22:56
  • 1
    @fespin You could verify it with `static_assert(std::is_same_v);`. There are also some compiler-specific ways to print the type of a variable, e.g. http://coliru.stacked-crooked.com/a/8b6d0e588c0eb0cb – HolyBlackCat Dec 23 '19 at 23:02
  • 2
    The type as a whole you're looping over is `Animal*`. `auto`, then, becomes `Animal*`, and we add to that a const reference. One way to think about it is to replace `Animal*` with `using animal_ptr = Animal*`; then, `const auto&` becomes `const animal_ptr&`: that is, the pointer is what is const, not whatever happens to be inside. – N. Shead Dec 23 '19 at 23:02
  • 1
    In your thought of `const Animal*`, the `const` would somehow need to travel _inside_ the pointer; that is, it would need to go inside `animal_ptr`. On the other hand, `Animal* const` is correct. (Remember that `const` applies to what is on its left, unless there's nothing on the left and then it applies to the thing directly on the right.) – N. Shead Dec 23 '19 at 23:07