static_cast унаследовал этот объект от базового класса в C++

При чтении статьи 27 "Минимизируйте приведение в Effective C++" говорится, что не пытайтесь использовать static_cast для приведения *this в производном классе к базовому классу. Это потому, что static_cast<Base>(*this) создаст временный объект базового класса. Я попробовал следующий пример, однако он всегда выводит 10 с использованием разных компиляторов, таких как clang 3.8 и gcc 4.9, 5.3.

Я ошибся?

  #include <iostream>

  class A {
  public:
    int a;
    virtual void foo() {std::cout << a << std::endl;}
  };

  class B : public A {
  public:
    int b;
    void foo ()  { static_cast<A>(*this).foo();}
  };


  int main () {
    B b;
    b.a = 10;
    b.foo();

    return 0;
  }

Вопрос в том, почему static_cast создаст временный объект.


person kwunlyou    schedule 28.04.2016    source источник
comment
Почему бы не напечатать 10?   -  person emlai    schedule 29.04.2016
comment
Какой вопрос? Какую проблему вы пытаетесь решить?   -  person Kerrek SB    schedule 29.04.2016
comment
Эффективный С++ говорит, что static_cast(*this) создаст временный объект базового класса. Итак, при таком предположении я думаю, что он должен выводить 0. Это правильно?   -  person kwunlyou    schedule 29.04.2016
comment
Почему он должен выводить 0? Временное создается как бы A(*this). Подумайте, какой конструктор он использует.   -  person Kerrek SB    schedule 29.04.2016
comment
@KerrekSB Спасибо. Теперь я понимаю. На самом деле, я создал этот проводной пример и хочу проверить, создан ли временный объект. Этот пример не подходит для этой цели.   -  person kwunlyou    schedule 29.04.2016


Ответы (2)


Более осмысленным примером может быть вот этот:

#include <iostream>

class A {
public:
    virtual void foo() { std::cout << "A" << std::endl; }
};

class B : public A {
public:
    virtual void foo() { std::cout << "B" << std::endl; }
    void bar ()  { static_cast<A>(*this).foo(); }
};

int main () {
    B b;
    b.bar();
}

Я ожидаю, что bar напечатает B, поскольку foo — это переопределенный метод. Вместо этого он печатает A.
Что ж, это правильно с точки зрения языка, не так хорошо с точки зрения разработчика, который ожидал совершенно другого результата.

Это работает, если вы используете вместо этого следующий класс:

class B : public A {
public:
    virtual void foo() { std::cout << "B" << std::endl; }
    void bar ()  { static_cast<A*>(this)->foo(); }
};

Также следующее работает, как и ожидалось (добавлено для ясности, спасибо @MORTAL в комментариях):

class B : public A {
public:
    virtual void foo() { std::cout << "B" << std::endl; }
    void bar ()  { static_cast<A&>(*this).foo(); }
};

В любом случае проблема, с которой вы столкнулись, называется нарезка.
Вот почему использование static_cast<A>(*this) не рекомендуется, если вы не знаете, что делаете.

Подробнее см. здесь.

person skypjack    schedule 28.04.2016
comment
Спасибо. На самом деле, я хочу создать пример, чтобы убедиться, что static_cast‹A›(*this) создаст временный объект. Но я сам накосячил. - person kwunlyou; 29.04.2016
comment
Пример в ответе фактически создает временный объект. - person skypjack; 29.04.2016
comment
я бы предпочел static_cast<A&>(*this).foo() вместо static_cast<A*>(this)->foo() - person MORTAL; 29.04.2016
comment
@MORTAL Да, они оба работают, как и ожидалось, я выбрал один для ответа. В любом случае, ответ обновлен. - person skypjack; 29.04.2016

Во-первых, вам не нужно приводить Derived -> Base, потому что это происходит автоматически. И да, static_cast создаст объект типа, к которому вы выполняете приведение. В вашем случае для включения полиморфизма вы можете использовать либо ссылки, либо указатели:

int main(){
    B b;
    A &a = b; // no explicit cast needed
    a.foo(); // will call B::foo

    //OR

    B *bPtr = new B;
    A *aPtr = bPtr; // no explicit cast needed
    aPtr->foo(); // same as above
}
person LibertyPaul    schedule 28.04.2016
comment
Спасибо. На самом деле, я хочу создать пример, чтобы убедиться, что static_cast‹A›(*this) создаст временный объект. Но я сам накосячил. - person kwunlyou; 29.04.2016