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:
Using a helper function:
template<class T> void destroy(T& t) { t.~T(); }
Using a decltype-specifier w/o a qualified-id. This one is tricky, as the type of
decltype(*wp)
isA::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
-> template
opt 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-specifiertemplate
opt 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.