Question

Suppose a class contains a type defined by a nested using whose destructor needs to be explicitly invoked. Is it necessary to use using to create a local type that doesn't include the namespace separator (::)?

In this contrived example, I want to call A::WeakPtr's destructor, like:

wp->~A::WeakPtr();

instead of like:

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

Is this doable? Here's a complete example.

#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";
}

It seems like there should be a way to defeat the :: namespace separator with a typename scattered in there somewhere, but it doesn't look like it's possible. Obviously it's not the end of the world if it's not possible, but my curio is getting the better of me.


UPDATE

As suggested by @DanielFrey and @DyP's fantastic answer, the correct syntax is indeed

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

but this doesn't work and is a bug (#12350) in clang++ (as of 2013-09-28).

Was it helpful?

Solution 2

The destructor call using a qualified-id here must consist of:

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

The postfix-expression here is wp, and the part after -> forms a single qualified-id (w/o the parens).

Grammatically, the following is also possible:

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

However, this second form is forbidden explicitly in [expr.prim.general]/9:

The form ~ decltype-specifier also denotes the destructor, but it shall not be used as the unqualified-id in a qualified-id.

The class-name in the first form can also be a typedef-name [class.name]/5:

A typedef-name (7.1.3) that names a class type, or a cv-qualified version thereof, is also a class-name.

For the lookup of this class-name after the ~, there's a special name lookup rule in [basic.lookup.qual]/5:

Similarly, in a qualified-id of the form:
     nested-name-specifieropt class-name :: ~ class-name
the second class-name is looked up in the same scope as the first.

This means that the second WeakPtr in A::WeakPtr :: ~WeakPtr should be found. It is a typedef-name naming a class, therefore a class-name, and it's looked up in the scope of A. gcc follows this rule, clang++3.4 does not.

Therefore, wp->A::WeakPtr :: ~WeakPtr(); as suggested by Daniel Frey (and my first, deleted comment/guess) should work.


Alternative approaches:

  1. Using a helper function:

    template<class T>
    void destroy(T& t)
    { t.~T(); }
    
  2. Using a decltype-specifier w/o a qualified-id. This one is tricky, as the type of decltype(*wp) is A::WeakPtr&, as *wp yields an lvalue. However, we can convert the the expression to a prvalue to get rid of the reference:

    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))();
    

Production:

Begin with the function call [expr.post]/1:

postfix-expression ( expression-listopt )

Where the postfix-expression here is produced via:

postfix-expression -> templateopt id-expression

This postfix-expression here maps to wp (in wp->~something()).

The id-expression contains the destructor "name" [expr.prim.general]:

id-expression:
     unqualified-id
     qualified-id

We do need a qualified-id here, so [expr.prim.general]/8:

qualified-id:
     nested-name-specifier templateopt unqualified-id
     :: identifier
     :: operator-function-id
     :: literal-operator-id
     :: template-id

Only the first one is of interest, so we look at unqualified-id s:

unqualified-id:
     identifier
     operator-function-id
     conversion-function-id
     literal-operator-id
     ~ class-name
     ~ decltype-specifier
     template-id

Where the two with a ~ can be used to call a destructor.

OTHER TIPS

Are you looking for

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

?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top