7.1 C++ compiler doesn't catch base class reference without public copy constructor

G

Guest

When compiled with Visual C++ .NET 2003 (only), the program below aborts as though no matching catch clause is present. If the copy constructor of A is made public, it successfully catches the exception instead. If I step into __CxxThrowException in debug/assembly mode, it looks like the pThrowInfo parameter created by the compiler excluded typeid(A) from pCatchableTypeArray. It would be reasonable to fail to catch A by value because of the inaccessible copy constructor, but it should be possible to catch A by reference. Looks like a bug in the new compiler

--
Andrew Scheple
aschepler . at . mrcday . nospam . dot . co
--

#include <iostream

class A
public
A() {
virtual ~A() {
protected
A(const A&) {
}

class B : public A
public
B() {
virtual ~B() {
B(const B&) {
}

int main()
try
throw B()
} catch (A&)
std::cout << "Caught an A object" << std::endl
return 1

return 0
 
H

Hendrik Schober

Andrew Schepler said:
When compiled with Visual C++ .NET 2003 (only), the program below
aborts as though no matching catch clause is present. If the
copy constructor of A is made public, it successfully catches the
exception instead. If I step into __CxxThrowException in
debug/assembly mode, it looks like the pThrowInfo parameter
created by the compiler excluded typeid(A) from pCatchableTypeArray.
It would be reasonable to fail to catch A by value because of the
inaccessible copy constructor, but it should be possible to
catch A by reference. Looks like a bug in the new compiler.

AFAIK exceptions must have public copy
ctors, otherwise the program is ill-
formed. I'd think that the compiler
should issue an error, but I am afraid
this is a QoI issue and not required.
Andrew Schepler

Schobi

--
(e-mail address removed) is never read
I'm Schobi at suespammers dot org

"Sometimes compilers are so much more reasonable than people."
Scott Meyers
 
G

Guest

----- Hendrik Schober wrote: ----
AFAIK exceptions must have public cop
ctors, otherwise the program is ill
formed. I'd think that the compile
should issue an error, but I am afrai
this is a QoI issue and not required

I don't really buy this, and I still think it's
compiler bug. I don't have access to the Standard
but here's some of my reasonings

1. The public, protected, and private keywords ar
clearly intended to control compile-time behavior
with as few changes to the resulting code as possible
(There is the caveat that a struct with mixed acces
classes for its data shouldn't be expected to look the sam
in C). If making a function protected is going to break somethin
purely C++, it should break at compile-time, not at run-time

2. The function in question would never be calle
(using any working compiler or if we changed i
to public). I could have just as well made it privat
and left it undefined. An exception object shoul
have a public copy constructor since the thro
statement is always required to make a copy (unles
the return value optimization applies?), but the exceptio
object here is of class B, not A, and B's cop
constructor is public

3. The compiler gets opportunities to complain i
situations where the copy constructor of A would b
called by the exception-handling mechanisms. If w
have
B b
A& a=b
throw a
then the throw statement is required to make an
object, slicing b, and throw it. This probably ough
to respect the accessibility of A's copy constructo
from the context of the throw. And if we hav
catch (A a
by value instead of by reference, then a copy of th
exception object must be made to initialize a i
the catch block, whether the exception object itsel
was really an A or a B. So this probably ought t
respect accessibility from the context of the catch
And to repeat myself again, all this can be don
by the compiler rather than the runtime

4. My inspection of the data internal exception-relate
functions are passing around seems to indicat
that at the throw statement, the compiler creates
symbol it mangles suggesting "throw info for B
containing an array of type_info pointers with
symbol suggesting "catchable types for B". And whe
the copy constructor for A isn't accessible, typeid(A
gets omitted from that array. Though it's true tha
a global function can't catch A by value, there hardl
seems to be any reason to leave it out. At th
throw statement, the compiler has no clue who migh
be catching it---maybe by reference, or mayb
by value but in a member of A or B or a friend function
Plus, unless the compiler can generate multipl
"catchable types for B" things, it will behave exactl
the same even if both the throw and catch are i
members of A. In my original program where
discovered this, the equivalent of A inherite
std::exception, and I was throwing B and attemptin
to catch std::exception&, both of which had publi
copy constructors, but the accessibility of A stoppe
the compiler from searching up the class inheritanc
and the exception refused to catch as std::exception&
So does Microsoft need to change th
mechanism entirely or add flags to track whethe
the object may be caught by value? No, the simples
fix is to always consider base classes as 'catchable
and just check accessibility at the throw statemen
and at the catch statement if by value
 
C

Carl Daniel [VC++ MVP]

Andrew said:
----- Hendrik Schober wrote: -----

I don't really buy this, and I still think it's a
compiler bug. I don't have access to the Standard,
but here's some of my reasonings:

Sorry, but Schobi is correct - your code is not required to work according
to the C++ standard (your reasonings aside).

15.3p17 says "When the exceptiondeclaration specifies a class type, ... The
copy constructor and destructor shall be accessible in the context of the
handler."

Make the copy-ctor public and move on.

-cd
 
D

Doug Harrison [MVP]

Carl said:
Sorry, but Schobi is correct - your code is not required to work according
to the C++ standard (your reasonings aside).

15.3p17 says "When the exceptiondeclaration specifies a class type, ... The
copy constructor and destructor shall be accessible in the context of the
handler."

Make the copy-ctor public and move on.

Yes, but his catch clause was:

catch (A&)

The type A& is "reference to A", not a class type. I think this is a bug in
VC.
 
G

Guest

----- Doug Harrison [MVP] wrote: ----
Carl Daniel [VC++ MVP] wrote
Sorry, but Schobi is correct - your code is not required to work accordin
to the C++ standard (your reasonings aside)
[...
The type A& is "reference to A", not a class type. I think this is a bug i
VC
Make the copy-ctor public and move on

While people with more authoritative arguments than mine sort this out
I'll just note that I did make the copy constructor public. Actually
the class in question in the real program was abstract anyway, which i
why I thought protected constructors would be appropriate in the firs
place, and it means that it doesn't actually matter since only subclasse
can call it anyway
 
H

Hendrik Schober

Doug Harrison said:
[...]
15.3p17 says "When the exceptiondeclaration specifies a class type, ... The
copy constructor and destructor shall be accessible in the context of the
handler."

Make the copy-ctor public and move on.

Yes, but his catch clause was:

catch (A&)

The type A& is "reference to A", not a class type. I think this is a bug in
VC.


Read my reply to Carl on this.

Schobi

--
(e-mail address removed) is never read
I'm Schobi at suespammers dot org

"Sometimes compilers are so much more reasonable than people."
Scott Meyers
 
H

Hendrik Schober

Carl Daniel said:
[...]
Sorry, but Schobi is correct - your code is not required to work according
to the C++ standard (your reasonings aside).

15.3p17 says "When the exceptiondeclaration specifies a class type, ... The
copy constructor and destructor shall be accessible in the context of the
handler."

Carl, re-reading the OP, I made up my
mind on this. :)
I am far from beeing a language laywer,
but I think in this case, in the catch
clause is not a class object, but a
reference to a class object. I think
this should make a difference.
Make the copy-ctor public and move on.

-cd

Schobi

--
(e-mail address removed) is never read
I'm Schobi at suespammers dot org

"Sometimes compilers are so much more reasonable than people."
Scott Meyers
 
H

Hendrik Schober

Hendrik Schober said:

BTW, I just checked Comeau and it
accepts the code in strict mode.

Schobi

--
(e-mail address removed) is never read
I'm Schobi at suespammers dot org

"Sometimes compilers are so much more reasonable than people."
Scott Meyers
 
C

Christoph Rabel

Carl said:
Sorry, but Schobi is correct - your code is not required to work according
to the C++ standard (your reasonings aside).

15.3p17 says "When the exceptiondeclaration specifies a class type, ... The
copy constructor and destructor shall be accessible in the context of the
handler."

Wrong quote, this doesnt apply here. But this one does:

15.1p5 states:

When the thrown object is a class object, and the copy constructor used
to initialize the temporary copy is not accessible, the program is
illformed (even when the temporary object could otherwise be eliminated).
Similarly, if the destructor for that object is not accessible, the
program is illformed (even when the temporary object could otherwise be
eliminated).

Christoph
 
H

Hendrik Schober

Christoph Rabel said:
[...]
Wrong quote, this doesnt apply here. But this one does:

15.1p5 states:
[...]

Look at the code again. The cctor and dtor are
accessible at the throw site. It's the catch
site that doesn't have access.
Christoph


Schobi

--
(e-mail address removed) is never read
I'm Schobi at suespammers dot org

"Sometimes compilers are so much more reasonable than people."
Scott Meyers
 
C

Christoph Rabel

Hendrik said:
Christoph Rabel said:
[...]
Wrong quote, this doesnt apply here. But this one does:

15.1p5 states:
[...]

Look at the code again. The cctor and dtor are
accessible at the throw site. It's the catch
site that doesn't have access.

Yes, I saw my error a few seconds after posting and tried to cancel it
immediately. I was a bit too late.

Christoph
 
C

Carl Daniel [VC++ MVP]

Andrew said:
The Standard quotes listed here so far make me think C++ acts
just like I thought it should (assuming 15.3p17 does not apply to
reference to class-type, only to "catch by value"). Just to make
things more interesting / clearer / more confusing / etc.,
here's a modified version of the program that
1. Doesn't directly reference the class with non-public ctor at
either the throw statement or catch site.
2. Has access to the non-public copy ctor at the catch site.
3. Doesn't rely on abort() to demonstrate the behavior, since
abort() could also be called by various other causes....

I think this demonstrates that compiling the throw statement does
something wrong. Incidentally, if the throw statement has access
to B's copy ctor (uncomment "throw C();" in B::f() ), catching by
reference does work.

As the final nail in the coffin - When compiled with the current Whidbey
alpha version, your code catches the object on the A& as it is now clear it
should.

So, it's a bug in 2003 that's already fixed for the next release.

-cd
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top