разница между static_cast <const A> (* это) и static_cast <const A &> (* это) - PullRequest
3 голосов
/ 27 сентября 2010

в следующем коде (взятом из действующего 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;
}

В этом коде, если вы измените static_cast с const TextBlock & на const TextBlock, это приведет к рекурсивному вызову неконстантной версии оператора []. Может кто-нибудь объяснить, в чем причина этого (почему const TextBlock не вызывает оператор функции-члена const []).

Ответы [ 5 ]

3 голосов
/ 23 марта 2015

Причина в том, что

const A a();

и

A b();

- это разные объекты, а в CPP непостоянные объекты не могут вызывать постоянные функции и наоборот;поэтому вам нужно, чтобы одна и та же функция была объявлена ​​дважды для const и неконстантных объектов соответственно.

cout << a[0] << endl;

допустимо, а

cout << b[0] << endl;

нет.По этой причине вы должны перегрузить оператор [] для неконстантного объекта.Чтобы избежать копирования кода, автор предлагает использовать функцию для константного объекта, отбрасывая его константность.По этой причине вы получаете:

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 &, а эта функция возвращает char &.Итак, отбросьте постоянство

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] );
 }

Теперь все готово.Вопрос в следующем: «Как

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

стало таким:

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

? Причина этого в том, что когда вы используете temp - вы делаете неявное приведение неконстантного к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]);

это вернет неконстантную ссылку на символ из этого объекта с константным вызовом, который вызывает перегруженный оператор [] :) Именно по этой причине выне может использовать

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

, потому что это неконстантный объект, поэтому он заставит его вызывать не затратную функцию (для перегрузки оператора []), которая вызовет бесконечную рекурсию.

Надеюсь, это имеет смысл.

2 голосов
/ 20 января 2016

Другая функция оператора [], которую мы назвали

Когда вы определяете два объекта, один является постоянным, а другой неконстантным, как это: 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] вызовет функцию non-const :

char &operator[](int);

Объект может быть изменен, также как и его член.Таким образом, эта функция возвращает тип char &, который можно изменить.


Что делается в неконстантной функции?

Итак, давайте посмотрим на связь между неконстантной функцией ипостоянная функция.В вашей программе неконстантная функция реализуется путем вызова константной функции.

Обычно неконстантный объект не может напрямую вызывать константную функцию.Но мы можем изменить атрибут на 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, значения которой мы можем изменить.

На самом деле оператор return в неконстантной функции можно заменить следующим образом:

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

tmp - это временная ссылка на * this (не временный объект).У нас есть неявное преобразование из TextBlock & в const TextBlock & здесь, тогда как в исходном операторе return явно есть преобразование.

Не забудьте добавить & в первый оператор , что означает, что мы используем ссылку на объект вместо реального объекта.Или он вызовет конструктор присваивания для генерации нового объекта.Если это произойдет, tmp не будет иметь ничего общего с *this.Это разные объекты, даже если они имеют одинаковое значение.Что бы мы ни изменяли в tmp, реальный объект, которым мы хотим управлять, не изменится!

1 голос
/ 27 сентября 2010

Приведенный ниже код работает - с возвращаемыми значениями, измененными на 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';
}
0 голосов
/ 27 сентября 2010

Ваши операторы имеют разные типы параметров

char & operator [] (std :: size_t position)
const char & operator [] (int index) const <- Также должно быть std :: size_t </p>

Это может быть решение, которое вы ищете.У примера из книги были разные типы для параметра?Помните, что приведение типов работает с возвращаемым значением.

char& operator[](std::size_t index)
{
  std::cout<<"non-const operator"<<std::endl;
  const TextBlock &  ref = *this;
  return const_cast<char&>(ref[index]);
}
0 голосов
/ 27 сентября 2010

char& operator[](std::size_t position) и char& operator[](std::size_t position) const различны. Обратите внимание на «const» после объявления функции. Первая - это неконстантная версия оператора, а вторая - это константная версия оператора. Неконстантная операторная функция вызывается, когда экземпляр этого класса неконстантный, а константная версия вызывается, когда объект константный.

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

Когда вы говорите (* this) [position] внутри неконстантной версии оператора, она вызывает неконстантную версию, которая снова вызывает неконстантную версию оператора, и она становится бесконечным циклом. Выполняя это приведение, вы по существу вызываете const-версию того же оператора.

РЕДАКТИРОВАТЬ: Хм .. кажется, что проблема не так, как кажется. У вас есть конструктор копирования для этого класса? Я предполагаю, что это то, что вызывает эту проблему.

РЕДАКТИРОВАТЬ 2: Я думаю, что у меня есть это. a [i] -> создает временную переменную при константном приведении без ссылки (const A) -> temp [position] -> создает временную переменную при константном приведении без ссылки -> temp2 [position] -> и она продолжается .....
в то время как переменная с константным приведением со ссылкой (констант A &) временная переменная не создается, следовательно, экранирует цикл смерти.

Чтобы было понятнее ... static_cast<const TextBlock>(*this)[position]; Разбивка вышеприведенного утверждения будет такой:

  • Преобразование * в постоянный текстовый блок
  • Создать временную переменную для хранения константного TextBlock (конструктор копирования называется передачей const TextBlock)
  • Вызвать оператор [] для временной переменной, которая НЕ является константой, потому что она была временной.
  • Временная переменная проходит вышеуказанный процесс.
...