【问题标题】:Why auto_ptr seems to breach private inheritance on Visual C++?为什么 auto_ptr 似乎违反了 Visual C++ 上的私有继承?
【发布时间】:2014-08-09 19:44:16
【问题描述】:

背景信息:这是在 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
}

为什么第一个(临时)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
}

再次,我希望代码不会编译,因为 Derived 是从 Base 私有继承的。

但是当我们创建一个临时的,它就起作用了。

我们不能把它归咎于 std::auto_ptr。

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

【问题讨论】:

  • 我的猜测是Ptr&lt;Base&gt;(new Derived); 不像它看起来那样做。看起来它创建了一个使用 new Derived 构造的临时类型 Ptr&lt;Base&gt;,但它实际上声明了一个函数或一些类似的废话。
  • @nwp:这很容易验证,只需从Derived 构造函数和析构函数中打印一些内容,就可以检查是否有实际代码执行。
  • 这个bug是一个类似的编译器错误,但是static_cast:connect.microsoft.com/VisualStudio/feedback/details/540343/…
  • FWIW,您甚至不需要模板来生成错误。 class Ptr 内部带有硬编码的 Base 会产生相同的行为。
  • 或者更精简的例子:class Base {}; class Derived : Base {}; struct S { S(Base *) {} }; int main() { S(new Derived); }

标签: c++ visual-studio private-inheritance


【解决方案1】:

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

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

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

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

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

4 执行的转换

  • const_cast (5.2.11),
  • static_cast (5.2.9),
  • static_cast 后跟 const_cast
  • reinterpret_cast (5.2.10),或
  • reinterpret_cast 后跟 const_cast

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

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

在您遇到的情况下,编译器将其解释为从Derived*auto_ptr&lt;Base&gt;static_cast,并且在static_cast 中,指向派生类类型对象的指针被转换为指针一个明确的基类类型。所以看起来标准允许它。

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

您可能希望将此报告为错误。从Csq 的评论中,我们了解到有一个related report,其中一个显式的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
}

【讨论】:

    猜你喜欢
    • 2017-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-28
    • 2022-01-04
    • 2013-10-23
    • 1970-01-01
    • 2019-05-24
    相关资源
    最近更新 更多