在以下代码中(取自有效的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;
}
在此代码中,如果从const TextBlock&amp;更改static_cast;对于const TextBlock,这会导致operator []的非const版本被递归调用。任何人都可以解释这背后的原因(为什么const TextBlock导致不调用const成员函数operator [])。
答案 0 :(得分:3)
原因是因为
const A a();
和
A b();
是不同的对象,而在CPP非常量对象中不能调用常量函数,反之亦然;因此,您需要为const和非const对象分别声明两次相同的函数。
cout << a[0] << endl;
是合法的,但
cout << b[0] << endl;
不是。 因此,您应该为非const对象重载[]运算符。为了避免复制代码,作者建议通过抛弃其constness来使用const对象的函数。因此,你得到:
char& operator[](std::size_t position)
{
return const_cast <char &>( static_cast <const A &>(*this) [position] );
}
换句话说,您只需将对象转换为const
char& operator[](std::size_t position)
{
const A temp = *this; //create a const object
//and assign current object to it
....
}
尝试使用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
}
得到一个错误,因为const函数的[]运算符返回const char&amp;并且此函数返回char&amp ;.因此,施放常数
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] );
}
现在你完成了。问题是:&#34;如何
const A temp = *this;
return const_cast <char &> (temp[position]);
成了这个:
return const_cast <char &> ( static_cast <const A &> (*this)[position]);
?原因是当你使用temp时 - 你正在对const对象进行非const的隐式转换,因此你可以替换:
const A temp = *this; // implicit cast
与
const A temp = static_cast <const A &> (*this) //explicit
这也有效:
const A temp = const_cast <const A &> (*this)
因为你可以做一个明确的演员 - 你不再需要一个临时演员,因此:
return const_cast <char &> (static_cast <const A &>(*this)[position]);
这将从这个const-casted对象返回一个非const引用,该对象调用一个重载的operator [] :)正是因为这个原因你不能使用
return const_cast <char &> ((*this)[position]);
因为这是一个非const对象;因此,它将调用非成本函数(重载operator []),这将导致无限递归。
希望它有意义。
答案 1 :(得分:2)
定义两个对象时,一个是const,另一个是非const,如下所示:
const TextBlock a("Hello");
TextBlock b("World");
当您使用a[0]
和b[0]
时,将调用不同类型的operator[]
。第一个 - a[0]
将调用 const 强>功能:
const char &operator[](int) const;
因为a
是一个const对象,所以它上面的每个操作都不能改变它的值。因此,当它使用运算符[]
时,将调用const版本并返回const char&
类型,这也意味着我们无法更改其成员的值。
另一方面,第二个 - b[0]
将调用非const 函数:
char &operator[](int);
对象可以更改,也可以更改其成员。所以这个函数返回一个char &
类型,可以修改。
让我们看看非const函数和const函数之间的联系。在你的程序中,通过调用const函数来实现非const函数。
通常,非const对象不能直接调用const函数。但我们可以通过static_cast<>
更改属性。因此,我们可以将对象b
转换为const TextBlock
,以便它可以调用const函数。
我们这样做
char &operator[](int index)
,这可以减少代码重用。
char & operator[](int position)
{
// const TextBlock tmp = *this;
// return const_cast<char &>(tmp[position]);
return
const_cast<char &>( (static_cast<const TextBlock &>(*this))[position] );
}
使用static_cast<const TextBlock &>(*this)
时,它会隐式定义一个临时对象,即TextBlock &
上的const TextBlock &
到*this
转换。
转换后,我们有一个临时对象,我们称之为 tmp 。所以我们可以使用tmp[0]
来调用const函数,这正是我们所做的。
从const函数返回后,我们得到一个类型为const char &
的值。但我们实际上想要的是char &
。所以我们使用const_cast<char &>
从返回值中删除const属性。在此之后,我们在char中得到一个非const引用,我们可以修改这些值。
实际上,非const函数中的return语句可以像这样替换:
const TextBlcok &tmp = *this;
return const_cast<char &>tmp[position];
tmp
是* this(不是临时对象)的临时引用。我们在这里隐式转换从TextBlock &
到const TextBlock &
,而在原始的return语句中有明确的转换。
请记住在第一个语句中添加&
,这表示我们使用对象的引用而不是真实对象。或者它将调用赋值构造函数来生成新对象。如果发生这种情况,tmp
将与*this
无关。即使它们具有相同的值,它们也是不同的对象。无论我们改变tmp,我们想要操作的实际对象都不会改变!
答案 2 :(得分:1)
以下代码有效 - 将返回值更改为char以避免在返回对已经消失的临时引用的引用时发现reko_t的问题 - 在g ++ 3.4.6上。
解释问题......
static_cast<const TextBlock>(*this)
...实际上与......相同。
const TextBlock temporary = *this;
...然后您将其编入索引并返回引用。但是,在使用引用时,临时值已从堆栈中消失。鉴于您正在返回此类引用,您的行为在技术上尚未定义。
您的编译器是否使用下面的代码? (在int
标准化的职位类型,以避免含糊不清)。
#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';
}
答案 3 :(得分:0)
char& operator[](std::size_t position)
和char& operator[](std::size_t position) const
不同。注意函数声明后的'const'。第一个是运算符的“非const”版本,第二个是运算符的const版本。当该类的实例为非const时调用非const运算符函数,当对象为const时调用const版本。
const A a;
char c = a[i]; // Calls the const version
A b;
char d = b[i]; // Calls the non-const version
当你在运算符的非const版本中说(* this)[position]时,它调用非const版本,它再次调用运算符的非const版本,它变成一个无限循环。通过执行该转换,您实际上是调用同一运算符的const版本。
编辑:嗯..似乎问题不像看起来那样。你有那个类的拷贝构造函数吗?我猜这是造成这个问题的原因。 编辑2:我想我已经知道了。 a [i] - &gt;在没有引用的情况下进行const转换时创建临时变量(const A) - &gt; temp [position] - &gt;在没有引用的情况下在const转换时创建临时变量 - &gt; temp2 [位置] - &gt;它继续.....static_cast<const TextBlock>(*this)[position];
上述陈述的细分将是:
答案 4 :(得分:0)
您的运营商有不同的参数类型
炭&安培; operator [](std :: size_t position)
const char&amp; operator [](int index)const&lt; - 也应该是std :: size_t
这可能是您正在寻找的解决方案。 本书的例子是否有不同类型的参数? 请记住,类型转换适用于返回值。
char& operator[](std::size_t index)
{
std::cout<<"non-const operator"<<std::endl;
const TextBlock & ref = *this;
return const_cast<char&>(ref[index]);
}