为什么auto_ptr似乎违反了Visual C++上的私有继承?

pae*_*bal 14 c++ visual-studio private-inheritance

背景信息:这是在Visual Studio 2008上检测到的,并在Visual Studio 2013上再次确认.G ++在代码中尖叫,而Visual以静默方式接受私有继承泄露.

因此,在Visual C++上,我们有以下代码:

class Base {};
class Derived : Base {};      // inherits privately. Adding explicitly the
                              //    keyword private changes nothing

int main()
{
   std::auto_ptr<Base>(new Derived) ;   // compiles, which is NOT EXPECTED
   std::auto_ptr<Base> p(new Derived) ; // Does not compile, which is expected
}
Run Code Online (Sandbox Code Playgroud)

为什么第一个(临时)auto_ptr会编译?我在调试中进入它,它确实完成了对公共继承应该做的事情(调用正确的构造函数等)

想知道问题是否与auto_ptr实现有关(我们永远不知道......),我减少了这个独立代码的问题:

class Base {};
class Derived : Base {};

template <typename T>
class Ptr
{
   T * m_p;

   public :
      Ptr(T * p_p)
         : m_p(p_p)
      {
      }
} ;

int main()
{
   Ptr<Base>(new Derived) ;   // compiles, which is NOT EXPECTED
   Ptr<Base> p(new Derived) ; // Does not compile, which is expected
}
Run Code Online (Sandbox Code Playgroud)

同样,我希望代码不能编译,因为Derived从Base私下继承.

但是当我们创建一个临时的时候,它就有用.

我们不能责怪它在std :: auto_ptr上.

我错过了标准(98或11或14)中的某些内容,或者这是一个错误?

小智 3

即使继承是私有的,在 C 风格和函数式转换中也允许进行Derived*- 到 -转换。Base*不,这并不意味着reinterpret_cast在这种情况下。

标准不允许这样做,但看起来几乎是允许的,所以这是一个微妙的错误。

5.2.3 显式类型转换(函数表示法)[expr.type.conv]

1 [...] 如果表达式列表是单个表达式,则类型转换表达式与相应的强制转换表达式 (5.4) 等效(在定义方面,并且如果在含义上定义)。[...]

5.4 显式类型转换(强制转换表示法)[expr.cast]

4 执行的转换

  • ( 5.2.11 const_cast),
  • ( static_cast5.2.9),
  • 一个static_cast后接一个const_cast
  • a reinterpret_cast(5.2.10),或
  • 一个reinterpret_cast后接一个const_cast

可以使用显式类型转换的强制转换表示法来执行。适用相同的语义限制和行为,但 static_cast在以下情况下执行 a 时,即使基类不可访问,转换也是有效的:

  • 指向派生类类型的对象的指针或派生类类型的左值或右值可以分别显式转换为指向明确基类类型的指针或引用;
  • [...]

在您遇到的情况下,编译器将其解释为static_castfrom Derived*to auto_ptr<Base>,并且在该情况下static_cast,指向派生类类型的对象的指针将转换为明确基类类型的指针。所以看起来标准允许这样做。

Derived*然而,从到 的转换是隐式的,它只是恰好作为显式不同转换的一部分Base*执行。所以最后,不,标准确实不允许。

您可能想将此报告为错误。从Csq的评论中,我们了解到有一个相关的报告,其中显式static_cast也允许这种转换,但并不完全相同。Derived*在这种情况下,从到 的转换Base*是显式的,但这里是隐式的,并且 Visual C++ 通常在隐式转换中拒绝这种转换。

请注意,在使用多个表达式的函数转换中,这种误解是不可能的:编译器正确地拒绝以下内容:

class Base { };
class Derived : Base { };

template <typename T>
class Ptr {
public:
  Ptr(T *a, T *b) { }
};

int main() {
  Ptr<Base>(new Derived, new Derived);
  // error C2243: 'type cast' : conversion from 'Derived *' to 'Base *' exists, but is inaccessible
}
Run Code Online (Sandbox Code Playgroud)