调用类型嵌套在类中的对象析构函数?

时间:2013-09-27 21:57:15

标签: c++11 destructor using placement-new typename

假设class包含由嵌套using定义的类型,其需要显式调用析构函数。是否有必要使用using创建不包含命名空间分隔符(::)的本地类型?

在这个人为的例子中,我想调用A::WeakPtr的析构函数,如:

wp->~A::WeakPtr();

而不是像:

using AWeakPtr = A::WeakPtr;
wp->~AWeakPtr()

这可行吗?这是一个完整的例子。

#include <cstdlib>
#include <iostream>
#include <memory>

struct A : std::enable_shared_from_this<A> {
  using SharedPtr = std::shared_ptr<A>;
  using WeakPtr = std::weak_ptr<A>;

  A()  { std::cout << __PRETTY_FUNCTION__ << "\n"; }
  ~A() { std::cout << __PRETTY_FUNCTION__ << "\n"; }
};

int
main() {
  {
    std::unique_ptr<A::WeakPtr, void(*)(void*)>
        uwp(static_cast<A::WeakPtr*>(std::malloc(sizeof(A::WeakPtr))), std::free);
    A::WeakPtr* wp = uwp.get();

    {
      auto sp = std::make_shared<A>();
      new(wp) A::WeakPtr(sp);

      if (wp->lock())
        std::cout << "Locked\n";
      else
        std::cout << "Unlocked\n";
    }

    if (wp->lock())
      std::cerr << "EUNPOSSIBLE\n";
    else
      std::cout << "Unable to obtain lock\n";

    // Need the following 'using' statement because the following is invalid syntax:
    // wp->~A::WeakPtr();
    using AWeakPtr = A::WeakPtr;
    wp->~AWeakPtr();
    // Is there a way to call A::WeakPtr without the using statement?
  }
  std::cout << "memory held by uwp has been free(3)'ed\n";
}

似乎应该有一种方法来打败::命名空间分隔符,其中typename分散在某处,但它看起来不太可能。显然,如果不可能的话,这不是世界末日,但是我的古玩对我来说变得越来越好。


更新

根据@DanielFrey和@ DyP的fantastic answer的建议,正确的语法确实是

wp->A::WeakPtr::~WeakPtr();

但这不起作用,并且是clang ++中的错误(#12350)(截至2013-09-28)。

2 个答案:

答案 0 :(得分:4)

您在寻找

吗?
wp->A::WeakPtr::~WeakPtr();

答案 1 :(得分:3)

此处使用 qualified-id 的析构函数调用必须包含:

postfix-expression -> nested-name-specifier ~ class-name ()

postfix-expression 此处为wp->之后的部分形成 qualified-id (没有parens)。

在语法上,以下也是可能的:

postfix-expression -> nested-name-specifier ~ decltype-specifier ()

但是,在[expr.prim.general] / 9中明确禁止使用第二种形式:

  

表单~ decltype-specifier也表示析构函数,但它不能用作 qualified-id 中的 unqualified-id

第一种形式的 class-name 也可以是 typedef-name [class.name] / 5:

  

命名类类型的 typedef-name (7.1.3)或 cv - 限定版本,也是类名< / em>的

要在~之后查找此类名,[basic.lookup.qual] / 5中有一个特殊的名称查找规则:

  

同样,在形式的 qualified-id 中:
   nested-name-specifier opt class-name :: ~ class-name
  第二个类名在与第一个相同的范围内查找。

这意味着应找到WeakPtr中的第二个A::WeakPtr :: ~WeakPtr。它是一个 typedef-name 命名一个类,因此是一个类名,它在A的范围内查找。 gcc遵循这个规则,clang ++ 3.4没有。

因此wp->A::WeakPtr :: ~WeakPtr(); suggested Daniel Frey {{3}}(我的第一个,删除的评论/猜测)应该有用。


替代方法:

  1. 使用辅助函数:

    template<class T>
    void destroy(T& t)
    { t.~T(); }
    
  2. 使用 decltype-specifier 没有 qualified-id 。这个很棘手,因为decltype(*wp)的类型是A::WeakPtr&,因为*wp会产生左值。但是,我们可以将表达式转换为prvalue以摆脱引用:

    wp->~decltype((A::WeakPtr)*wp)();
    // alternatively, w/o explicitly mentioning the type:
    wp->~decltype((std::remove_pointer<decltype(wp)>::type)*wp)();
    // simpler, using a helper function again:
    template<class T>  T helper(T const&);
    wp->~decltype(helper(*wp))();
    

  3. 生产:

    从函数调用[expr.post] / 1开始:

      

    postfix-expression ( 表达式列表 opt )

    这里的 postfix-expression 是通过以下方式生成的:

      

    postfix-expression -> template opt id-expression

    后缀表达式此处映射到wpwp->~something())。

    id-expression包含析构函数“name”[expr.prim.general]:

      

    ID-表达:
      不合格-ID
      合格-ID

    我们在这里需要 qualified-id ,所以[expr.prim.general] / 8:

      

    合格-ID:
      嵌套名称说明符 template opt unqualified-id
      :: 标识符
      :: operator-function-id
      :: literal-operator-id
      :: template-id

    只有第一个是有意义的,所以我们看一下 unqualified-id

      

    不合格-ID:
      标识符
      操作员功能-ID
      转换函数-ID
      字面运营商-ID
      ~ 类名
      ~ decltype-specifier
      模板id

    可以使用~的两个来调用析构函数。