difference between static_cast<const A>(*this) and static_cast<const A&>(*this)

旧城冷巷雨未停 提交于 2019-11-30 23:46:02

The reason is because

const A a();

and

A b();

are different objects, and in CPP non-constant objects cannot call constant functions and vice versa; therefore, you need the same function to be declared twice for a const and non-const object respectively.

cout << a[0] << endl;

is legal, but

cout << b[0] << endl;

is not. For that reason you should overload [] operator for non-const object. In order to avoid copying the code, author suggest using a function for const object through casting away its constness. For that reason you get:

char& operator[](std::size_t position)
{
     return const_cast <char &>( static_cast <const A &>(*this) [position] );
}

in other words, you just convert your object to const

char& operator[](std::size_t position)
{
     const A temp = *this;      //create a const object 
                                    //and assign current object to it
     ....
}

try to use []operator of const obj

char& operator[](std::size_t position)
{
     const A temp = *this;      //create a const object 
                                    //and assign current object to it
     return temp[position];     // call an overloaded operator [] 
                                // of the const function
}

get an error because []operator of const function returns const char& and this function returns char&. Thus cast constness away

char& operator[](std::size_t position)
{
     const A temp = *this;      //create a const object 
                                //and assign current object to it
     return const_cast <char &>( temp[position] );
 }

Now you are done. The question is: "How

const A temp = *this;
return const_cast <char &> (temp[position]);

became this:

return const_cast <char &> ( static_cast <const A &> (*this)[position]);

? The reason for that is when you are using temp - you are making implicit cast of non-const to a const object, thus you can replace:

const A temp = *this;                                // implicit cast

with

const A temp = static_cast <const A &> (*this)        //explicit  

this also works:

const A temp = const_cast <const A &> (*this) 

and since you can make an explicit cast - you don't need a temp anymore, thus:

return const_cast <char &> (static_cast <const A &>(*this)[position]);

this will return a non-const ref to a char from this const-casted object that calls an overloaded operator[] :) Exactly for that reason you cannot use

return const_cast <char &> ((*this)[position]);

because this is a not-const object; therefore, it will make it to call not cost function (to overload operator[]) which will cause an infinite recursion.

Hope it makes sense.

Different operator[] function we called

When you define two object, one is const and another is non-const like this: const TextBlock a("Hello"); TextBlock b("World");

When you use a[0] and b[0], different kind of operator[] will be called.The first one -- a[0], will call the const function:

const char &operator[](int) const;

Because a is a const object, each operation on it can't change its value. So when it use the operator [],the const version will be called and return a const char& type, which also means we can't change its members' values.

On the other hand, the second one -- b[0] will call the non-const function:

char &operator[](int);

The object can be changed, also can its member. So this function returns a char & type, which can be modified.


What's being done in non-const function ?

So let's see the connection between non-const function and const function. In your program, non-const function is realized by calling const function.

Normally, a non-const object can not call the const function directly. But we can change the attribute by static_cast<>. Thus we can transform object b into const TextBlock,so that it can call the const function.

We do this in char &operator[](int index) ,which can reduce code reusing.

char & operator[](int position) { // const TextBlock tmp = *this; // return const_cast<char &>(tmp[position]); return const_cast<char &>( (static_cast<const TextBlock &>(*this))[position] ); }

When using static_cast<const TextBlock &>(*this), it implicitly define a temporary object which is the TextBlock & to const TextBlock & conversion on *this.

After the conversion, we have a temporary object and we called it tmp. So we can use tmp[0] to called the const function, which is what we exactly do.

After returning from the const function, we get a value which type is const char &. But we we actually want to is char &. So we use const_cast<char &> remove the const attribute from the return value. After this, we get a non-const reference in char, which values can be modified by us.

Actually, the return statement in non-const function can be replaced like this:

const TextBlcok &tmp = *this; return const_cast<char &>tmp[position];

The tmp is the temporary reference to *this(Not a temporary object). We have an implicitly conversion from TextBlock & to const TextBlock & here, whereas there is an explicitly conversion in the original return statement.

Remember to add the & in the first statement, which represents that we use a reference to object instead of an real object. Or it will call an assignment constructer to generate a new object. If it happens, tmp will have nothing to do with *this. They are different object even if they have the same value. Whatever we change tmp, the actual object we want to operate will not change!

The code below works - with the return values changed to char to avoid the issue reko_t found with returning a reference to an already-gone temporary - on g++ 3.4.6.

To explain the problem...

static_cast<const TextBlock>(*this)

...is effectively the same as...

const TextBlock temporary = *this;

...which you then index into and return a reference. But, that temporary is gone from the stack by the time that reference is used. Given you were returning such a reference, your behaviour was technically undefined.

Does your compiler work with the code below? (type of position standardised at int to avoid ambiguity).

#include <iostream>

struct A  
{ 

  char operator[](int position)         // now just calls const op[] 
  { 
    return 
        static_cast<const A>(*this)     // add const to *this's type; 
          [position];                   // call const version of op[] 
  } 

  const char operator[](int index) const 
  { 
    return x_[index];
  } 

  char x_[10];
};

int main()
{
  A a;
  strcpy(a.x_, "hello!");
  const A& ca = a;
  std::cout << a[0] << ca[1] << a[2] << ca[3] << a[4] << ca[5] << '\n';
}

char& operator[](std::size_t position) and char& operator[](std::size_t position) const are different. Note the 'const' after the function declaration. The first one is the 'non-const' version of the operator, while the second is the const version of the operator. The non-const operator function gets called when the instance of that class is non-const and const version gets called when the object is const.

const A a;
char c = a[i]; // Calls the const version
A b;
char d = b[i]; // Calls the non-const version

When you say (*this)[position] inside the non-const version of the operator, it calls the non-const version, which again calls the non-const version of the operator and it becomes an infinite loop. By doing that casting you're essentially calling the const version of the same operator.

EDIT: Hmm.. seems like the problem is not as it seems. Do you have a copy-constructor for that class? I'm guessing it's that which is causing this issue.

EDIT2: I think I've got it. a[i] -> creates temporary variable upon const cast without reference (const A) -> temp[position] -> creates temporary variable upon const cast without reference -> temp2[position] -> and it continues.....
while the one with const cast with reference (const A&) the temporary variable is not created, hence escaping the death-loop.

To make it more clear... static_cast<const TextBlock>(*this)[position]; Break-down of the above statement would be:

  • Convert *this to const TextBlock
  • Create a temporary variable to hold the const TextBlock (copy-constructor called passing const TextBlock)
  • Call the operator[] on the temporary variable, which is NOT const because it was temporary.
  • Temporary variable goes through the above process.

Your operators have different inparameter types

char& operator[](std::size_t position)
const char& operator[](int index) const <- Should also be std::size_t

This might be the solution you are looking for. Did the example from the book have different types for the inparameter? Remember that type casting works on the return value.

char& operator[](std::size_t index)
{
  std::cout<<"non-const operator"<<std::endl;
  const TextBlock &  ref = *this;
  return const_cast<char&>(ref[index]);
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!