Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Explicit destructor call - problem with typedef from other namespace

162 views
Skip to first unread message

PiotrN

unread,
May 13, 2012, 5:49:46 AM5/13/12
to
Hello,

I started similar thread in past week:

http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/4445dc89046ac1cd

but now I want your advice / explanation about this thing:

Imagine simple code:

namespace a {
class A { public: virtual ~A() { cout << "~A()"; }};
typedef A TA;
class B : public A { public: virtual ~B() { cout << "~B()"; }};
}

and 3 different usages:

1) This is classic usage, produces expected output: ~B()~A()
int main() {
char buf[100];
a::A *p = new (buf) a::B;
p->~A();
}

2) This doesn't compile "20:error: expected class-name before ‘(’
token".
int main() {
char buf[100];
a::TA *p = new (buf) a::B;
p->~TA(); //line 20
}

3) This compiles but produces unexpected output: ~A().
I understand this because it is not "virtual" call of destructor.
int main() {
char buf[100];
a::TA *p = new (buf) a::B;
p->a::TA::~TA();
}

Please tell me if case #2 is just gcc error (gcc-4.3.4) or C++
standard defines this. If this is standard - is there any rationale
for this?
If "using namespace a" added to case #2 - everything works fine.

Thanks in advance,
Piotr


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Ulrich Eckhardt

unread,
May 13, 2012, 9:53:17 AM5/13/12
to
PiotrN wrote:
> Imagine simple code:
>
> namespace a {
> class A { public: virtual ~A() { cout << "~A()"; }};
> typedef A TA;
> class B : public A { public: virtual ~B() { cout << "~B()"; }};
> }
>
> and 3 different usages:
[...]
> 2) This doesn't compile "20:error: expected class-name before ‘(’
> token".
> int main() {
> char buf[100];
> a::TA *p = new (buf) a::B;
> p->~TA(); //line 20
> }

I'd expect this, though you could argue differently. The point is that
using the "a::A*" as a base, there is no "TA" that can be found. To
some extent, anything beginning with the tilde can only be a
destructor, but the name is still required, and TA is neither a fully
qualified name nor can it be resolved. Note that the reason this
behaviour could be argued is that ADL would also find an according
function in the namespace enclosing the parameters.


> 3) This compiles but produces unexpected output: ~A().
> I understand this because it is not "virtual" call of destructor.
> int main() {
> char buf[100];
> a::TA *p = new (buf) a::B;
> p->a::TA::~TA();
> }

Right, here you are explicitly calling just one specific destructor,
overriding the dynamic dispatch.

How about "p->~a::TA()", have you tried that?


> If "using namespace a" added to case #2 - everything works fine.

Adding "using a::TA" should also do the trick without pulling in
everything from namespace "a".


Good luck!

Uli

Johannes Schaub

unread,
May 13, 2012, 9:56:13 AM5/13/12
to
Am 13.05.2012 11:49, schrieb PiotrN:
> Hello,
>
> I started similar thread in past week:
>
>
http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/4445dc89046ac1cd
>
> but now I want your advice / explanation about this thing:
>
> Imagine simple code:
>
> namespace a {
> class A { public: virtual ~A() { cout<< "~A()"; }};
> typedef A TA;
> class B : public A { public: virtual ~B() { cout<< "~B()"; }};
> }
>
> and 3 different usages:
>
> 1) This is classic usage, produces expected output: ~B()~A()
> int main() {
> char buf[100];
> a::A *p = new (buf) a::B;
> p->~A();
> }
>

This works because it finds the injected class name of the class "a::A"
(for every class definition you write, a class-name is introduced both
in the surrounding scope (that name is is a::A), and into the class
itself (that is "class a::A::A").

> 2) This doesn't compile "20:error: expected class-name before ‘(’
> token".
> int main() {
> char buf[100];
> a::TA *p = new (buf) a::B;
> p->~TA(); //line 20
> }
>

When you use an unqualified name, the compiler has to know what that
name means. To allow you to use a name of the current scope (which is
often the case in templates in calls like "p->~T()" when T is a template
parameter) the name is looked up in the current scope and in the scope
of "p" separately. If at least one of these lookups finds a class-name
of the class of p, it's fine.

In this case neither scope has a "TA" defined so this fails (as far as
the Standard is concerned, "TA" which was used for defining p is just a
type alias and has no bearing on the type of p, just on how you name
it). You can make this work by providing "TA" in the surrounding scope

int main() {
char buf[100];
a::TA *p = new (buf) a::B;

using a::TA;
p->~TA();
}

I think another way is to abuse the fact that a "X<T>" is also a
class-name and can be used after "~" (note that "X<T>::type", as would
be when you would use "std::conditional<>" or "boost::identity<>", is
not a class-name but one with a qualifier before it), so you can also say

template<typename T>
using Identity = T;

int main() {
char buf[100];
a::TA *p = new (buf) a::B;
p->~Identity<a::TA>();
}

> 3) This compiles but produces unexpected output: ~A().
> I understand this because it is not "virtual" call of destructor.
> int main() {
> char buf[100];
> a::TA *p = new (buf) a::B;
> p->a::TA::~TA();
> }
>

This works because when you qualify the destructor id like

p-> qualifier :: type :: ~type

Then the last "type" name is looked up in the same scope where the first
"type" was found. So the last "TA" is not looked up in the class "a::A"
(where it would not be found), but in namespace "a" where it will be found.

I believe that your understanding is correct, this inhibits the virtual
calling since you use a qualified name.

Note that this lookup is nutoriously complicated and is subject of
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#399 (so
arguably "in the same scope where the first 'type' was found" looks a
bit simplified above but I believe it catches the common cases). If you
can I would avoid qualified constructor calls.
0 new messages