How is it that a new
expression in a program can throw a bad_alloc
error despite there being no #include <new>
(since this error is defined in the <new>
header)?
From 3.7.4. of N3337:
The library provides default definitions for the global allocation and deallocation functions. Some global allocation and deallocation functions are replaceable (18.6.1). A C++ program shall provide at most one definition of a replaceable allocation or deallocation function. Any such function definition replaces the default version provided in the library (17.6.4.6). The following allocation and deallocation functions (18.6) are implicitly declared in global scope in each translation unit of a program.
void* operator new(std::size_t); void* operator new[](std::size_t); void operator delete(void*); void operator delete[](void*);
These implicit declarations introduce only the function names
operator new
,operator new[]
,operator delete
, andoperator delete[]
. [ Note: The implicit declarations do not introduce the namesstd
,std::size_t
, or any other names that the library uses to declare these names. Thus, a new-expression, delete-expression or function call that refers to one of these functions without including the header<new>
is well-formed. However, referring tostd
orstd::size_t
is ill-formed unless the name has been declared by including the appropriate header. —end note ] Allocation and/or deallocation functions can also be declared and defined for any class
This still isn't clear to me. The implicit declarations use std::size_t
but do not introduce them (and the same must be the case for bad_alloc
)? And std::size_t
doesn't need to be introduced before a new
expression can be used? Can any sense be made of how this is, or do I have to take it at face value?
Your quote says that those global functions exist and are implicitly declared as described. Thus when you invoke new
, the global function in the standard library is called. The implementation of the global function new
is the one throwing std::bad_alloc
, and that implementation had access to <new>
at the time of compilation, thus knows how to throw an std::bad_alloc
. Your code doesn't need to know what a std::bad_alloc
is, unless you're trying to catch it. But other than catching it, this is like you calling any other function from some other library that may be throwing some arbitrary exception. You don't need to know about the details of that exception unless you're trying to catch it, but that doesn't stop the callee from being able to throw it.
The name std::size_t
is just a typedef for some other integer type, perhaps unsigned long
or unsigned long long
. The compiler knows the real parameter type for new
, even if the name size_t
isn't visible.
Similar for bad_alloc
. The runtime code throwing a bad_alloc
surely includes the <new>
header, even if your program does not.
A C++ standard header is allowed to include any other C++ header. Therefore you can get implementation-dependent access to std::bad_alloc
without including <new>
, and to std::size_t
with including <cstddef>
et al. I'm too lazy to look it up in your edition of the standard, but in the N4296 draft it's in §17.6.5.2:
A C++ header may include other C++ headers.
You can try this with a compiler.
int main()
{
std::size_t x; // error
std::bad_alloc y; // error
}
Now let's add a completely unrelated #include
:
#include <complex>
int main()
{
std::size_t x; // probably not an error anymore, depends on compiler
std::bad_alloc y; // probably not an error anymore, depends on compiler
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With