clarification of specifics of P0137 - c++

In the following code I have been meticulous in the following of the standard's words (plus in the light of the wording of P0137) on object lifetimes.
Note that all memory allocation is through suitably-aligned storage of type unsigned char, as per P0137.
Note also that Foo is a POD, with a trivial constructor.
Questions:
A. If I have misunderstood the standard, and there is any UB here, please kindly point it out (or alternatively confirm that there is no UB)
B. Are the initialisations at A, B, C, D, E, F strictly necessary in light of the fact that the construction is trivial, and performs no actual initialisation. If so, please indicate which part of the standard contradicts or clarifies [object.lifetime] in this regard.
code:
#include <memory>
// a POD with trivial constructor
struct Foo
{
int x;
};
struct destroy1
{
void operator()(Foo* p)
{
// RAII to guarantee correct destruction order
auto memory = std::unique_ptr<unsigned char[]>(reinterpret_cast<unsigned char*>(p));
p->~Foo(); // A
}
};
std::unique_ptr<Foo, destroy1> create1()
{
// RAII to guarantee correct exception handling
auto p = std::make_unique<unsigned char[]>(sizeof(Foo));
auto pCandidate = reinterpret_cast<Foo*>(p.get());
new (pCandidate) Foo(); // B
return std::unique_ptr<Foo, destroy1>(reinterpret_cast<Foo*>(p.release()),
destroy1());
}
struct call_free
{
void operator()(void *p) const { std::free(p); }
};
using malloc_ptr = std::unique_ptr<unsigned char, call_free>;
struct destroy2
{
void operator()(Foo *pfoo) const {
// RAII to guarantee correct destruction order
auto memory = malloc_ptr(reinterpret_cast<unsigned char*>(pfoo));
pfoo->~Foo(); // C
}
};
std::unique_ptr<Foo, destroy2> create2()
{
// RAII to guarantee correct exception handling
auto p = malloc_ptr(reinterpret_cast<unsigned char*>(std::malloc(sizeof(Foo))));
auto pCandidate = reinterpret_cast<Foo*>(p.get());
new (pCandidate) Foo(); // D
return std::unique_ptr<Foo, destroy2>(reinterpret_cast<Foo*>(p.release()),
destroy2());
}
struct nodelete {
void operator()(Foo * p) {
p->~Foo(); // E
}
};
std::shared_ptr<Foo> provide()
{
alignas(Foo) static unsigned char storage[sizeof(Foo)];
auto make = [] {
auto p = reinterpret_cast<Foo*>(storage);
new (p) Foo (); // F
return std::shared_ptr<Foo>(p, nodelete());
};
static std::shared_ptr<Foo> pCandidate = make();
return pCandidate;
}
int main()
{
auto foo1 = create1();
auto foo2 = create2();
auto foo3 = provide();
foo1->x = 1;
foo2->x = 2;
foo3->x = 3;
}

On create1
std::unique_ptr<Foo, destroy1>(reinterpret_cast<Foo*>(p.release()), destroy1());
This doesn't work, because you're using the wrong pointer.
p.release() thinks it points to an unsigned char[]. However, that's not the object you want to point to. What you want to point to is the object that lives inside this array, the Foo you've created.
So you are now subject to [basic.life]/8. The gist of that is that you can only use the previous pointer as a pointer to the new object if they are of the same type. Which they're not in your case.
Now, I could tell you to launder the pointer, but the more reasonable way to handle this is to just store the pointer returned by the placement-new call:
auto p = std::make_unique<unsigned char[]>(sizeof(Foo));
auto ret = std::unique_ptr<Foo, destroy1>(new(p.get()) Foo(), destroy1());
p.release();
return ret;
That pointer will always be correct.
Your use of of placement-new is not optional. [intro.object]/1 tells us:
An object is created by a definition (3.1), by a new-expression (5.3.4), when implicitly changing the active member of a union (9.3), or when a temporary object is created (4.4, 12.2).
When you allocate an unsigned char[], that's the object you have created in that storage. You cannot simply pretend that it is a Foo, just because Foo is an aggregate. [intro.object]/1 doesn't allow that. You must explicitly create that object via one of the mechanisms listed above. Since you can't use a definition, union member activation, or temporary objects with arbitrary memory buffers to create objects from existing storage, the only recourse you have to create objects is a new-expression.
Specifically, placement-new.
As for delete1, you do need a custom deleter, since the default deleter will call delete on the Foo pointer. Your code is as follows:
auto memory = std::unique_ptr<unsigned char[]>(reinterpret_cast<unsigned char*>(p));
p->~Foo();
unsigned char[] has some special logic to it, in terms of how it behaves when objects are allocated in their storage, thanks to [intro.object]/3-4. If the object entirely overlays the storage of the unsigned char[], then it functions as if the object were allocated within the array. That means that the unsigned char[] is still technically there; it has not destroy the byte array.
As such, you can still delete the byte array, which your code here does.
On create2
This is also wrong, due to further violations of [basic.life]/8. A fixed version would be similar to the above:
auto p = malloc_ptr(reinterpret_cast<unsigned char*>(std::malloc(sizeof(Foo))));
auto ret std::unique_ptr<Foo, destroy2>(new(p.get()) Foo(), destroy2());
p.release();
return ret;
Unlike new-expressions, malloc never creates an object via [intro.object]/1; it only acquires storage. As such, placement-new is again required.
Similarly, free just releases memory; it doesn't deal with objects. So your delete2 is essentially fine (though the use of malloc_ptr there makes it needlessly confusing).
On provide
This has the same [basic.life]/8 problems that the rest of your examples have:
alignas(Foo) static unsigned char storage[sizeof(Foo)];
static auto pCandidate = std::shared_ptr<Foo>(new(storage) Foo(), nodelete());
return pCandidate;
But other than that, it's fine (so long as you don't break it elsewhere). Why? That's complex.
[basic.start.term]/1 tells us that static objects are destroyed in the reverse order of their initialization. And [stmt.decl]/4 tells us that block-scoped static objects are initialized in the order they are encountered in a function.
Therefore, we know that pCandidate will be destroyed before storage. So long as you don't keep a copy of that shared_ptr in a static variable, or otherwise fail to destroy/reset all such shared objects before termination, you should be fine.
That all being said, using blocks of unsigned char is really pre-C++11. We have std::aligned_storage and std::aligned_union now. Use them.

If you take seriously Core Issue 1776 and the never voted for idea that "that malloc alone is not sufficient to create an object", then you have to take seriously these ideas:
unions were not usable without UB in C++ until quite recently, as the lifetime of their members was not defined
string literals cannot be examined, as their lifetime is never started
and many other deeper more difficult controversies and contradictions, like what is a lvalue, an object, is lifetime a property of a preexisting object (that exists outside it's life), etc.
Yet I don't see people taking at least the two bullet points seriously. Why would the claim in the DR be taken seriously then?

"Core Issue 1776: Replacement of class objects containing reference members" is based on an obvious and serious interpretation error, and as such should be dismissed. The error is here:
Drafting note: this maintains the status quo that malloc alone is not
sufficient to create an object
This goes against the status quo that "malloc alone" is indeed sufficient to create an object, as malloc returns suitably aligned storage, which is and has always been sufficient to create an object.
A core issue is not the standard. It is an opinion about the standard. That opinion is in error.

Related

Initializing an array of trivially_copyable but not default_constructible objects from bytes. Confusion in [intro.object]

We are initializing (large) arrays of trivially_copiable objects from secondary storage, and questions such as this or this leaves us with little confidence in our implemented approach.
Below is a minimal example to try to illustrate the "worrying" parts in the code.
Please also find it on Godbolt.
Example
Let's have a trivially_copyable but not default_constructible user type:
struct Foo
{
Foo(double a, double b) :
alpha{a},
beta{b}
{}
double alpha;
double beta;
};
Trusting cppreference:
Objects of trivially-copyable types that are not potentially-overlapping subobjects are the only C++ objects that may be safely copied with std::memcpy or serialized to/from binary files with std::ofstream::write()/std::ifstream::read().
Now, we want to read a binary file into an dynamic array of Foo. Since Foo is not default constructible, we cannot simply:
std::unique_ptr<Foo[]> invalid{new Foo[dynamicSize]}; // Error, no default ctor
Alternative (A)
Using uninitialized unsigned char array as storage.
std::unique_ptr<unsigned char[]> storage{
new unsigned char[dynamicSize * sizeof(Foo)] };
input.read(reinterpret_cast<char *>(storage.get()), dynamicSize * sizeof(Foo));
std::cout << reinterpret_cast<Foo *>(storage.get())[index].alpha << "\n";
Is there an UB because object of actual type Foo are never explicitly created in storage?
Alternative (B)
The storage is explicitly typed as an array of Foo.
std::unique_ptr<Foo[]> storage{
static_cast<Foo *>(::operator new[](dynamicSize * sizeof(Foo))) };
input.read(reinterpret_cast<char *>(storage.get()), dynamicSize * sizeof(Foo));
std::cout << storage[index].alpha << "\n";
This alternative was inspired by this post. Yet, is it better defined? It seems there are still no explicit creation of object of type Foo.
It is notably getting rid of the reinterpret_cast when accessing the Foo data member (this cast might have violated the Type Aliasing rule).
Overall Questions
Are any of these alternatives defined by the standard? Are they actually different?
If not, is there a correct way to implement this (without first initializing all Foo instances to values that will be discarded immediately after)
Is there any difference in undefined behaviours between versions of the C++ standard?
(In particular, please see this comment with regard to C++20)
What you're trying to do ultimately is create an array of some type T by memcpying bytes from elsewhere without default constructing the Ts in the array first.
Pre-C++20 cannot do this without provoking UB at some point.
The problem ultimately comes down to [intro.object]/1, which defines the ways objects get created:
An object is created by a definition, by a new-expression, when implicitly changing the active member of a union, or when a temporary object is created ([conv.rval], [class.temporary]).
If you have a pointer of type T*, but no T object has been created in that address, you can't just pretend that the pointer points to an actual T. You have to cause that T to come into being, and that requires doing one of the above operations. And the only available one for your purposes is the new-expression, which requires that the T is default constructible.
If you want to memcpy into such objects, they must exist first. So you have to create them. And for arrays of such objects, that means they need to be default constructible.
So if it is at all possible, you need a (likely defaulted) default constructor.
In C++20, certain operations can implicitly create objects (provoking "implicit object creation" or IOC). IOC only works on implicit lifetime types, which for classes:
A class S is an implicit-lifetime class if it is an aggregate or has at least one trivial eligible constructor and a trivial, non-deleted destructor.
Your class qualifies, as it has a trivial copy constructor (which is "eligible") and a trivial destructor.
If you create an array of byte-wise types (unsigned char, std::byte, or char), this is said to "implicitly create objects" in that storage. This property also applies to the memory returned by malloc and operator new. This means that if you do certain kinds of undefined behavior to pointers to that storage, the system will automatically create objects (at the point where the array was created) that would make that behavior well-defined.
So if you allocate such storage, cast a pointer to it to a T*, and then start using it as though it pointed to a T, the system will automatically create Ts in that storage, so long as it was appropriately aligned.
Therefore, your alternative A works just fine:
When you apply [index] to your casted pointer, C++ will retroactively create an array of Foo in that storage. That is, because you used the memory like an array of Foo exists there, C++20 will make an array of Foo exist there, exactly as if you had created it back at the new unsigned char statement.
However, alternative B will not work as is. You did not use new[] Foo to create the array, so you cannot use delete[] Foo to delete it. You can still use unique_ptr, but you'll have to create a deleter that explicitly calls operator delete on the pointer:
struct mem_delete
{
template<typename T>
void operator(T *ptr)
{
::operator delete[](ptr);
}
};
std::unique_ptr<Foo[], mem_delete> storage{
static_cast<Foo *>(::operator new[](dynamicSize * sizeof(Foo))) };
input.read(reinterpret_cast<char *>(storage.get()), dynamicSize * sizeof(Foo));
std::cout << storage[index].alpha << "\n";
Again, storage[index] creates an array of T as if it were created at the time the memory was allocated.
My first question is: What are you trying to achieve?
Is there an issue with reading each entry individually?
Are you assuming that your code will speed up by reading an array?
Is latency really a factor?
Why can't you just add a default constructor to the class?
Why can't you enhance input.read() to read directly into an array? See std::extent_v<T>
Assuming the constraints you defined, I would start with writing it the simple way, reading one entry at a time, and benchmark it.
Having said that, that which you describe is a common paradigm and, yes, can break a lot of rules.
C++ is very (overly) cautious about things like alignment which can be issues on certain platforms and non-issues on others. This is only "undefined behaviour" because no cross-platform guarantees can be given by the C++ standard itself, even though many techniques work perfectly well in practice.
The textbook way to do this is to create an empty buffer and memcpy into a proper object, but as your input is serialised (potentially by another system), there isn't actually a guarantee that the padding and alignment will match the memory layout which the local compiler determined for the sequence so you would still have to do this one item at a time.
My advice is to write a unit-test to ensure that there are no issues and potentially embed that into the code as a static assertion. The technique you described breaks some C++ rules but that doesn't mean it's breaking, for example, x86 rules.
Alternative (A): Accessing a —non-static— member of an object before its lifetime begins.
The behavior of the program is undefined (See: [basic.life]).
Alternative (B): Implicit call to the implicitly deleted default constructor.
The program is ill-formed (See: [class.default.ctor]).
I'm not sure about the latter. If someone more knowledgeable knows if/why this is UB please correct me.
You can manage the memory yourself, and then return a unique_ptr which uses a custom deleter. Since you can't use new[], you can't use the plain version of unique_ptr<T[]> and you need to manually call the destructor and deleter using an allocator.
template <class Allocator = std::allocator<Foo>>
struct FooDeleter : private Allocator {
using pointer = typename std::allocator_traits<Allocator>::pointer;
explicit FooDeleter(const Allocator &alloc, len) : Allocator(alloc), len(len) {}
void operator()(pointer p) {
for (pointer i = p; i != p + len; ++i) {
Allocator::destruct(i);
}
Allocator::deallocate(p, len);
}
size_t len;
};
std::unique_ptr<Foo[], FooDeleter<>> create(size_t len) {
std::allocator<Foo> alloc;
Foo *p = nullptr, *i = nullptr;
try {
p = alloc.allocate(len);
for (i = p; i != p + len; ++i) {
alloc.construct(i , 1.0f, 2.0f);
}
} catch (...) {
while (i > p) {
alloc.destruct(i--);
}
if (p)
alloc.deallocate(p);
throw;
}
return std::unique_ptr<Foo[], FooDeleter<>>{p, FooDeleter<>(alloc, len)};
}

Can I overwrite a const object via placement-new?

Basic.life/8 tells us that we can use the storage occupied by an object to create a new one after its lifetime has ended and use its original name to refer to it unless:
the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and [...]
emphasis mine
But, just below that we can see a note saying that:
If these conditions are not met, a pointer to the new object can be obtained from a pointer that represents the address of its storage by calling std​::​launder
This explains the purposes of std::launder. We can have a class type that has const members and use placement-new to create a new object there with different internal values.
What surprises me is the first part of the first quote. It clearly indicates that if the storage is const (not necessarily contains const members, but the whole object is declared as const), we cannot use it to refer to a new object as is, but it may imply that std::launder may fix it.
But how would we create such object? The simplest example fails:
const auto x = 5;
new (&x) auto(10);
It's because we cannot use const void* as a buffer for placement-new. We could const_cast it, but casting away a "true" constness is Undefined Behaviour. I believe this is also the case here.
I would understand if there simply was a prohibition of using const objects as placement-new buffer, but if that would be the case, what would be the purpose of the emphasised part of the first quote? Can we use reuse a truly const object's storage for a different object?
Apparently all it took was to look just 2 items below the part of the standard I linked to. Basic.life/10 tells us that:
Creating a new object within the storage that a const complete object with static, thread, or automatic storage duration occupies, or within the storage that such a const object used to occupy before its lifetime ended, results in undefined behavior.
And it comes with an example:
struct B {
B();
~B();
};
const B b;
void h() {
b.~B();
new (const_cast<B*>(&b)) const B; // undefined behavior
}
which ultimately leads me to a conclusion that it's illegal to use placement-new on a memory occupied by a truly const object. Thus, I believe that the note mentioned in the question (in reference to point 8) is faulty - it should exclude the case in question.
A const object can have non const pointers to its location.
struct bar{ int x=5; };
struct foo{
const bar b;
};
foo f;
::new (&f) foo{{3}};
here I have a const int f.b.x which I destroy and construct a new one with value 3.
char buff[sizeof(foo)];
foo const* pf=::new(buff)foo;
foo const* pf2=::new(buff)foo{{3}};
Might also be able to do it with unions.

Can't launder(void), therefore free() is UB?

I am trying to determine what the strictly conforming pattern for using memory allocators with C++17 is. Specifically, moving away from the working in practice UB pattern:
example * foo = (example*)malloc(sizeof(example)); // no object here, don't use foo!
Therefore placement new into the allocated memory. Something like:
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <new>
struct example { // alignment <= max_align_t
example(int x) : x(x) {}
~example() {}
int x;
static void *operator new(size_t, example *p) { return p; }
};
int main() {
example *ex = nullptr;
{
void *m = malloc(sizeof(example));
if (!m) {
return 1;
}
ex = new (reinterpret_cast<example *>(m)) example(42);
// if one failed to store the result of the new expression, can retrieve it
// from m via launder
ex = std::launder(reinterpret_cast<example *>(m));
// now abandon all further instances of 'void *m'
}
// ... do some well defined things with the example object at *ex
All good, we have a properly constructed object. How do we free it?
// ...
{
ex->~example(); // lifetime has ended
// UB? We don't have a void* to pass to free
// free(ex);
// equivalent to above, it's still not a void*
// free(reinterpret_cast<void*>(ex));
// error, new defines: void launder(void*) = delete
// free(std::launder(reinterpret_cast<void*>(ex)));
// error: void pointer argument to __builtin_launder is not allowed
// free( __builtin_launder(reinterpret_cast<void*>(ex)));
// Quoting the rule book, 21.6.4 [ptr.launder]
// The program is ill-formed if T is a function type or cv void
// Out of options, guess it has to be free
free(ex);
}
}
void * doesn't have the aliasing properties of a char*. Can't even do arithmetic on void*.
'Free' is a difficult thing to search for. I can't see special case magic for free, but even if there is such, it won't help with platform_allocate/platform_deallocate pairs.
On the basis that launder definitely doesn't permit one to get the void* back, free(ex) in the above is presumably the right thing to do.
What makes free(ex) well defined?
Why is launder(void) forbidden? A pointer optimisation barrier applied at the end of the malloc() implementation seems a great idea.
What makes free(ex) well defined?
free requires a pointer whose address is one directly returned by a call to malloc. You have provided that. Why wouldn't it work?
Why is launder(void) forbidden?
Because it makes no sense. std::launder<T> returns a pointer to an object of type T within its lifetime whose address is the address of the given pointer. void is an incomplete type; there cannot be objects of type void. Therefore, std::launder<void> cannot possibly function.
Memory allocation functions typically do not deal with pointers to objects. They deal in pointers to memory. The only thing they care about is whether the address of that pointer is one they can deal with.

Inside the copy constructor of shared_ptr

I have some confusion about the shared_ptr copy constructor. Please consider the following 2 lines:
It is a "constant" reference to a shared_ptr object, that is passed to the copy constructor so that another shared_ptr object is initialized.
The copy constructor is supposed to also increment a member data - "reference counter" - which is also shared among all shared_ptr objects, due to the fact that it is a reference/pointer to some integer telling each shared_ptr object how many of them are still alive.
But, if the copy constructor attempts to increment the reference counting member data, does it not "hit" the const-ness of the shared_ptr passed by reference? Or, does the copy constructor internally use the const_cast operator to temporarily remove the const-ness of the argument?
The phenomenon you're experiencing is not special to the shared pointer. Here's a typical primeval example:
struct Foo
{
int * p;
Foo() : p(new int(1)) { }
};
void f(Foo const & x) // <-- const...?!?
{
*x.p = 12; // ...but this is fine!
}
It is true that x.p has type int * const inside f, but it is not an int const * const! In other words, you cannot change x.p, but you can change *x.p.
This is essentially what's going on in the shared pointer copy constructor (where *p takes the role of the reference counter).
Although the other answers are correct, it may not be immediately apparent how they apply. What we have is something like this:
template <class T>
struct shared_ptr_internal {
T *data;
size_t refs;
};
template <class T>
class shared_ptr {
shared_ptr_internal<T> *ptr;
public:
shared_ptr(shared_ptr const &p) {
ptr = p->ptr;
++(ptr->refs);
}
// ...
};
The important point here is that the shared_ptr just contains a pointer to the structure that contains the reference count. The fact that the shared_ptr itself is const doesn't affect the object it points at (what I've called shared_ptr_internal). As such, even when/if the shared_ptr itself is const, manipulating the reference count isn't a problem (and doesn't require a const_cast or mutable either).
I should probably add that in reality, you'd probably structure the code a bit differently than this -- in particular, you'd normally put more (all?) of the code to manipulate the reference count into the shared_ptr_internal (or whatever you decide to call it) itself, instead of messing with those in the parent shared_ptr class.
You'll also typically support weak_ptrs. To do this, you have a second reference count for the number of weak_ptrs that point to the same shared_ptr_internal object. You destroy the final pointee object when the shared_ptr reference count goes to 0, but only destroy the shared_ptr_internal object when both the shared_ptr and weak_ptr reference counts go to 0.
It uses an internal pointer which doesn't inherit the contests of the argument, like:
(*const_ref.member)++;
Is valid.
the pointer is constant, but not the value pointed to.
Wow, what an eye opener this has all been! Thanks to everyone that I have been able to pin down the source of confusion to the fact that I always assumed the following ("a" contains the address of "b") were all equivalent.
int const *a = &b; // option1
const int *a = &b; // option2
int * const a = &b; // option3
But I was wrong! Only the first two options are equivalent. The third is totally different.
With option1 or option2, "a" can point to anything it wants but cannot change the contents of what it points to.
With option3, once decided what "a" points to, it cannot point to anything else. But it is free to change the contents of what it is pointing to. So, it makes sense that shared_ptr uses option3.

Size of class instance

I'm working with a class for which the new operator has been made private, so that the only way to get an instance is to write
Foo foo = Foo()
Writing
Foo* foo = new Foo()
does not work.
But because I really want a pointer to it, I simulate that with the following :
Foo* foo = (Foo*)malloc(sizeof(Foo));
*foo = Foo();
so that can test whether the pointer is null to know whether is has already been initialized.
It looks like it works, from empirical tests, but is it possible that not enough space had been allocated by malloc ? Or that something else gets funny ?
--- edit ---
A didn't mention the context because I was not actually sure about why they the new operator was disabled. This class is part of a constraint programming library (gecode), and I thought it may be disabled in order to enforced the documented way of specifying a model.
I didn't know about the Concrete Data Type idiom, which looks like a more plausible reason.
That allocation scheme may be fine when specifying a standard model --- in which everything is specified as CDTs in the Space-derived class --- but in my case, these instance are each created by specific classes and then passed by reference to the constructor of the class that reprensents the model.
About the reason i'm not using the
Foo f;
Foo *pf = &f;
it would be like doing case 1 below, which throws a "returning reference to local variable" warning
int& f() { int a=5; return a; } // case 1
int& f() { int a=5; int* ap=&a; return *ap; }
int& f() { int* ap=(int*)malloc(sizeof(int)); *ap=5; return *ap; }
this warning disappears when adding a pointer in case 2, but I guess it is because the compiler loses tracks.
So the only option left is case 3 (not mentioning that additionaly, ap is a member of a class that will be initialized only once when f is called, will be null otherwise, and is the only function returning a reference to it. That way, I am sure that ap in this case when lose its meaning because of the compilier optimizing it away (may that happen ?)
But I guess this reaches far too much beyond the scope of the original question now...
Don't use malloc with C++ classes. malloc is different from new in the very important respect that new calls the class' constructor, but malloc does not.
You can get a pointer in a couple ways, but first ask yourself why? Are you trying to dynamically allocate the object? Are you trying to pass pointers around to other functions?
If you're passing pointers around, you may be better off passing references instead:
void DoSomething(Foo& my_foo)
{
my_foo.do_it();
}
If you really need a pointer (maybe because you can't change the implementation of DoSomething), then you can simply take the pointer to an automatic:
Foo foo;
DoSomething(&foo);
If you need to dynamically allocate the Foo object, things get a little trickier. Someone made the new operation private for a reason. Probably a very good reason. There may be a factory method on Foo like:
class Foo
{
public:
static Foo* MakeFoo();
private:
};
..in which case you should call that. Otherwise you're going to have to edit the implementation of Foo itself, and that might not be easy or a good thing to do.
Be careful about breaking the Concrete Data Type idiom.
You are trying to circumvent the fact that the new operator has been made private, i.e. the Concrete Data Type idiom/pattern. The new operator was probably made private for specific reasons, e.g. another part of the design may depend on this restriction. Trying to get around this to dynamically allocate an instance of the class is trying to circumvent the design and may cause other problems or other unexpected behavior. I wouldn't suggest trying to circumvent this without studying the code thoroughly to ensure you understand the impact to other parts of the class/code.
Concrete Data Type
http://users.rcn.com/jcoplien/Patterns/C++Idioms/EuroPLoP98.html#ConcreteDataType
Solutions
...
Objects that represent abstractions that live "inside" the program, closely tied to the computational model, the implementation, or the programming language, should be declared as local (automatic or static) instances or as member instances. Collection classes (string, list, set) are examples of this kind of abstraction (though they may use heap data, they themselves are not heap objects). They are concrete data types--they aren't "abstract," but are as concrete as int and double.
class ScopedLock
{
private:
static void * operator new (unsigned int size); // Disallow dynamic allocation
static void * operator new (unsigned int size, void * mem); // Disallow placement new as well.
};
int main (void)
{
ScopedLock s; // Allowed
ScopedLock * sl = new ScopedLock (); // Standard new and nothrow new are not allowed.
void * buf = ::operator new (sizeof (ScopedLock));
ScopedLock * s2 = new(buf) ScopedLock; // Placement new is also not allowed
}
ScopedLock object can't be allocated dynamically with standard uses of new operator, nothrow new, and the placement new.
The funny thing that would happen results from the constructor not being called for *foo. It will only work if it is a POD (simple built-in types for members + no constructor). Otherwise, when using assignment, it may not work out right, if the left-hand side is not already a valid instance of the class.
It seems, you can still validly allocate an instance on the heap with
Foo* p = ::new Foo;
To restrict how a class instance can be created, you will probably be better off declaring the constructor(s) private and only allow factory functions call them.
Wrap it:
struct FooHolder {
Foo foo;
operator Foo*() { return &foo; }
};
I don't have full understanding of the underlying code. If other things are ok, the code above is correct. Enough space will be allocated from malloc() and anything funny will not happen. But avoid using strange code and work straighforward:
Foo f;
Foo *pf = &f;