什么时候在C++中上传非法?

Con*_*ley 56 c++ class derived base downcast

我很确定我理解上传和下传之间的一般区别,特别是在C++中.我知道我们不能总是向下转换因为将基类指针强制转换为派生类指针会假定所指向的基类对象具有派生类的所有成员.

在学期的早些时候,我的教授告诉全班同学,在C++中升级有时也是非法的,但我似乎错过了我的笔记中的原因,我不记得这是什么时候发生的.

什么时候在C++中升级是非法的?

AnT*_*AnT 48

如果"非法"是指形成不良,那么如果基类无法访问或含糊不清则属于非法行为.

  • 例如,当基类是私有时,它是不可访问的.

    class A {};
    class B : A {};
    ...
    B b;
    A *pa = &b; // ERROR: base class is inaccessible
    
    Run Code Online (Sandbox Code Playgroud)

    请注意,即使在C++ 11中,C风格的强制转换也可以"突破"访问保护并执行正式的正确向上转换

    A *pa = (A *) &b; // OK, not a `reinterpret_cast`, but a valid upcast
    
    Run Code Online (Sandbox Code Playgroud)

    当然,应该避免这种用法.

  • 如果源类型包含目标类型的多个基础子对象(通过多重继承),则不明确.

    class A {};
    class B : public A {};
    class C : public A {};
    class D : public B, public C {};
    
    D d;
    A *pa = &d; // ERROR: base class is ambiguous
    
    Run Code Online (Sandbox Code Playgroud)

    在这种情况下,可以通过明确地"行走"所需的上行路径和中间上行到基点不再模糊的点来执行上行扫描.

    B* pb = &d;
    A* pa = pb; // OK: points to 'D::B::A' subobject
    
    Run Code Online (Sandbox Code Playgroud)

  • 在轨道上的@Lightness Races:因为它是一种破坏语言非常基本原则的黑客.当基础不可访问时,该语言的autors面临着一个艰难的选择:要么将其设置为`reinterpret_cast`(如果基本访问权限发生变化,这会产生微妙且几乎无法检测到的错误的可能性),或使其成为保护 - 失败`static_cast `(这是他们选择的).两种变体都很可怕. (7认同)
  • 为什么要"不惜一切代价避免",为什么这"当然"? (5认同)
  • 国际海事组织"不惜一切代价"过度陈述情况.是的,通常应该避免这种情况,但是会有更糟糕的情况,所以如果归结为选择,一些成本可能是可以接受的. (3认同)
  • @AndreyT:在最新的两个_standards_中引入了一些功能和结构,这些功能和结构违背了语言的基本原则.除了最可怕的黑客之外,我不会购买"避免_all_费用"的论据.无论如何,我的评论的重点是你在答案中没有写出任何理由和断言; 也许你现在可以这样做? (3认同)
  • @Lightness Races in Orbit:我不知道的是为什么在这种情况下他们没有使其格式错误。显然,他们希望保留 C 样式转换的遗留属性,这对于指针转换“始终有效”。 (2认同)

Che*_*Alf 17

如果基类不明确(通过不同路径继承两次或更多次),则无法在一个步骤中进行向上转换.

如果基类不可访问,则转发的唯一方法是使用C样式转换.这是该演员的一个特例,它是唯一可以完成这项工作的人.从本质上讲,它的行为static_cast不受可访问性的限制.


Standardese.

C++11§5.4/ 4:

" ...在[C cast]中执行a static_cast在以下情况下,即使基类不可访问,转换也是有效的:

  • 指向派生类类型的对象的指针或派生类类型的左值或右值可以分别显式转换为指针或对明确基类类型的引用;
  • 指向派生类类型成员的指针可以显式转换为指向非明确的非虚基类类型成员的指针;
  • 指向明确的非虚基类类型的对象的指针,明确的非虚基类类型的glvalue,或指向非明确的非虚基类类型的成员的指针可以被显式转换为指针,a引用或指向派生类类型成员的指针.

歧义的例子:

struct Base {};
struct M1: Base {};
struct M2: Base {};
struct Derived: M1, M2 {};

auto main() -> int
{
    Derived d;
    //static_cast<Base&>( d );                      //! Ambiguous
    static_cast<Base&>( static_cast<M2&>( d ) );    // OK
}
Run Code Online (Sandbox Code Playgroud)

无法访问的基础示例,在演员阵容中进行(通常)地址调整:

struct Base { int value; Base( int x ): value( x ) {} };

class Derived
    : private Base
{
public:
    virtual ~Derived() {}       // Just to involve an address adjustment.
    Derived(): Base( 42 ) {}
};

#include <iostream>
using namespace std;

auto main() -> int
{
    Derived d;
    Base& b = (Base&) d;
    cout << "Derived at " << &d << ", base at " << &b << endl;
    cout << b.value << endl;
};
Run Code Online (Sandbox Code Playgroud)

  • +1 特别是对于第二个示例。尽管我会从本质上删除,因为您给出了无法访问的基础的演员表的确切定义。 (2认同)

Ded*_*tor 10

有两种情况,在C++中编译不正确(在编译时诊断):

  1. 无法访问有问题的基类:

    class base {};
    class derived : base {};
    
    int main() {
        derived x;
        base& y = x; // invalid because not accessible.
        // Solution: C-style cast (as static_cast without access-check)
        base& y1 = (base&)x;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 有问题的基类子对象不是明确的:

    class base {};
    struct A1 : base {};
    struct A2 : base {};
    
    struct derived : A1, A2 {};
    int main() {
        derived x;
        base& y = x; // invalid because ambiguous.
        // Solution 1, scope resolution:
        base& y1 = static_cast<A1::base&>(x);
        base& y2 = static_cast<A2::base&>(x);
        // Solution 2, intermediate unambiguous steps:
        A1& a1 = x;
        A2& a2 = x;
        base& ya1 = a1;
        base& ya2 = a2;
    }
    
    Run Code Online (Sandbox Code Playgroud)