This article from boost: Error and Exception Handling puts forth the following program code:
#include <iostream>
struct my_exc1 : std::exception {
char const* what() const throw();
};
struct my_exc2 : std::exception {
char const* what() const throw();
};
struct your_exc3 : my_exc1, my_exc2 {};
int main() {
try {
throw your_exc3();
} catch(std::exception const& e) {}
catch(...) {
std::cout << "whoops!" << std::endl;
}
}
When compiling with g++ (GCC) 5.2.0, I get the following
> g++ -std=c++11 custom_exception.cpp
/tmp/ccmbzPOk.o: In function `my_exc1::my_exc1()':
custom_exception.cpp:(.text._ZN7my_exc1C2Ev[_ZN7my_exc1C5Ev]+0x19): undefined reference to `vtable for my_exc1'
/tmp/ccmbzPOk.o: In function `my_exc1::~my_exc1()':
custom_exception.cpp:(.text._ZN7my_exc1D2Ev[_ZN7my_exc1D5Ev]+0xd): undefined reference to `vtable for my_exc1'
/tmp/ccmbzPOk.o: In function `my_exc2::my_exc2()':
custom_exception.cpp:(.text._ZN7my_exc2C2Ev[_ZN7my_exc2C5Ev]+0x19): undefined reference to `vtable for my_exc2'
/tmp/ccmbzPOk.o: In function `my_exc2::~my_exc2()':
custom_exception.cpp:(.text._ZN7my_exc2D2Ev[_ZN7my_exc2D5Ev]+0xd): undefined reference to `vtable for my_exc2'
/tmp/ccmbzPOk.o:(.rodata._ZTV9your_exc3[_ZTV9your_exc3]+0x20): undefined reference to `my_exc1::what() const'
/tmp/ccmbzPOk.o:(.rodata._ZTV9your_exc3[_ZTV9your_exc3]+0x48): undefined reference to `my_exc2::what() const'
/tmp/ccmbzPOk.o:(.rodata._ZTI9your_exc3[_ZTI9your_exc3]+0x18): undefined reference to `typeinfo for my_exc1'
/tmp/ccmbzPOk.o:(.rodata._ZTI9your_exc3[_ZTI9your_exc3]+0x28): undefined reference to `typeinfo for my_exc2'
collect2: error: ld returned 1 exit status
I have seen the identical technique used elsewhere, suggesting to me that this should compile (and link) silently. (As an example, I cite Anthony Williams C++ Concurrency in Action p. 45 where he inherits from std::exception
to make empty_stack
for the thread-safe stack example.)
I have tried to #include <exception>
and despite the fact that this isn't a C++ library problem, I even tried the -lstdc++
flag on the advice of people with similar problems---out of desperation.
I understand that in std::exception
, what()
is virtual, meaning I should define it---so I'm not sure why it should compile in the first place, but I'm frustrated that it apparently does for other people.
My questions are two: (1) What is the problem, and why does it work for others? (2, conditionally) New to C++, I should also ask what is a good way to implement what()
(assuming I will have to) in the most minimal way, since I don't actually want to pass a string with my exception. I don't need to inherit from deeper in the hierarchy, such as std::runtime_error
.
According to C++14 (N3936) [basic.def.odr]/3:
A virtual member function is odr-used if it is not pure.
So my_exc1::what()
and my_exc2::what()
are odr-used, even though they are never called. Then we have [basic.def.odr]/4:
Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required.
So this whole program has undefined behaviour, but the compiler/linker is not required to diagnose it.
The rationale for this lax requirement is to make a linker's job easier: if the linker happens to be able link without including a call to this function or whatever, then it can do so; the C++ standard does not require the linker to do some sort of whole program analysis to determine whether all odr-used functions have bodies.
So this code is bugged and it should have bodies for both of those functions. It also should have #include <exception>
. For the people who compiled and executed this code; their iostream
included exception
(which is permitted but not required), and their linker manifested the undefined behaviour as appearing to link correctly.
To provide a body it is as simple as:
char const *what() const throw() { return ""; }
(assuming you're fine doing it inline). Of course you could return some other fixed string such as "my_exc1"
. Note that if you only want to return ""
then you do not need to re-declare what()
at all.