1

I've encountered interesting issue doing homework assignment for implementing collections with STL-like iterators. Here's minimal example:

class const_iterator {
public:
  const int* const_p = new int();

  const int& operator*()
  {
     return *const_p;
  }
};

class iterator : public const_iterator
{
public:
  int*& p = const_cast<int*&>(const_iterator::const_p);

  int& operator*()
{
  return *p;
  //return const_cast<int&>(*const_iterator::const_p); //-alt solution, no reference member
  }
}; 

It was my understanding that references are a kind of "syntactic sugar" for a programmer - they work like "aliases", take no memory if necessary, and get optimized out. However, running sizeof() it turns out base class takes 4 bytes, and derived iterator - 8 bytes.

Fiddling some more, I've found that reference members always take 4 bytes. Even if - as in this case - they point directly to a member of base class. I don't see a reason why compiler would keep them as a pointer here? If we can access this reference than surely we can access the object it's refering to?

Furthermore, references declared as local variables in function scope also add additional instructions in the disassembly - that was obviously inspected without any optimizations and all debug symbols included. Do those get optimized away in Release builds?

int main() {
    auto& amIAnAliasOrDoIExist(Long::Qualified::Very<Nasty>.Name); 
    return amIAnAliasOrDoIExist; 
}

Is reference ever a "pure alias" (taking no memory in stack/heap)? Under what circumstance can compiler optimize reference variable in such a way? What does standard have to say about it?

Also, since declaring such reference member wastes space (in my homework), is there any performance hit with doing const_casts at every call to operator*()?

MkjG
  • 144
  • 2
  • 15
  • 2
    For starters, this horribly breaks aliasing. The correct way to do this is for the `const_iterator` base class to have a `protected` pointer to whatever's being iterated, and return only `const` reference to the iterated-over value. Then the `iterator` subclass can simply use the pointer directly. No ugly, disgusting, `const_cast`s should be needed. – Sam Varshavchik Apr 22 '18 at 13:57
  • @SamVarshavchik Aside of access modifiers (this is just a minimal example), if i return `T&` in iterator and `const T&` in const_iterator - one of those classes have to do const_cast, doesn't it? Keeping `T* p` in const_iterator would require `const_cast(const_iterator::p)` in `const_iterator::operator*()`, wouldn't it? I don't see it much different from this solution. Why does it break aliasing? – MkjG Apr 22 '18 at 14:04
  • @SamVarshavchik Or is cast `const int -> int`, somehow 'more evil' than `int -> const int`? Since the first one can be done implicitly? – MkjG Apr 22 '18 at 14:10
  • No explicit `const_cast` is needed to convert a mutable reference to a const reference. Whoever told you that you need to do a `const_cast(foo)` if `foo` is a `T &` was giving you wrong information. – Sam Varshavchik Apr 22 '18 at 14:14

0 Answers0