问题
in the following code ( taken from effective C++ ):
class A
{
....
char& operator[](std::size_t position) // now just calls const op[]
{
return
const_cast<char&>( // cast away const on op[]'s return type;
static_cast<const TextBlock&>(*this) // add const to *this's type;
[position] // call const version of op[]
);
}
const char& operator[](int index) const
{
...
}
}
//complete example, tested with VC 2010
#include<iostream>
#include<string>
class TextBlock
{
public:
TextBlock(std::string st):text(st){};
TextBlock(char* cstr): text(cstr){};
TextBlock(const TextBlock& r)
{
std::cout<<"copy constructor called"<<std::endl;
}
char& operator[](int index)
{
std::cout<<"non-const operator"<<std::endl;
return const_cast<char&>(static_cast<const TextBlock>(*this)[index]);
}
const char& operator[](int index) const
{
std::cout<<"const operator"<<std::endl;
return text[index];
}
private:
std::string text;
};
int main()
{
TextBlock rt("hello");
std::cout<<rt[0]<<std::endl;
}
In this code, if you change the static_cast from const TextBlock& to const TextBlock, this results in non-const version of operator[] getting called recursively. Can anyone explain what's the reason behind this ( why const TextBlock results in not calling const member function operator[] ).
回答1:
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.
回答2:
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!
回答3:
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';
}
回答4:
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.
回答5:
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]);
}
来源:https://stackoverflow.com/questions/3802556/difference-between-static-castconst-athis-and-static-castconst-athis