Making a smart pointer which works with incomplete types

J

Juha Nieminen

Alf said:
No, it doesn't; that's up to you.

Ok, boost::shared_ptr supports the user giving it a custom deleter
(rather than the automatically created one), which is why it needs a
member pointer.

My original question still stands, though.
 
K

Kai-Uwe Bux

Alf said:
* Kai-Uwe Bux:

If you're able to assign to a static pointer to destroy function, then
you're able to have that function as template parameter.

That's a different challenge.

The static pointer buys you nothing whatsoever.

It's just nonsensical complexity.

So far, I have yet to see a working proposal that beats tr1::shared_ptr
resource-wise and avoids a static pointer.

It doesn't.

Does that mean:

a) It doesn't fall short.
b) It doesn't require deleteX to be defined.

The code you actually posted neither satisfies (a) nor (b). The code that
you asked us to read instead (the same man that admitted to not reading
code:) may or may not qualify.

Given any concrete example of a general principle you can nearly always
find another application of the same general principle where the concrete
example is less than ideal. In this case you've found a more constrained,
a more specialized case (where the type is complete at instantiation
point) which can be solved more *easily*. And complain that the offered
example was too general!

I think we've played that game before.

It's silly to the extreme.

If you want to refuse the challenge, just say so. I have no intention on
making you take it.


Anyway, I just gave you my interpretation of Juha. Whether that
interpretation is correct, we can leave up to him. (It appears, though,
that he is not happy with the code you provided.)


Best

Kai-Uwe Bux
 
J

Juha Nieminen

Kai-Uwe Bux said:
Anyway, I just gave you my interpretation of Juha. Whether that
interpretation is correct, we can leave up to him. (It appears, though,
that he is not happy with the code you provided.)

My original question was not really a "how do I make a smart pointer
which works with incomplete types?", but "does this work?"

My question was prompted from the realization that I could use the
same technique as boost::shared_ptr uses, but using a static function
pointer rather than a non-static one, and I was wondering why this
doesn't seem to be a common technique (in other words, maybe I have
missed some error in the code?)

Granted, by using a static function pointer it will not be possible to
define a deleter on a per-smartpointer basis, but that's not what I'm
worried about. I'm more worried about the space that pointer requires.
Making it static removes it from the smart pointer object, making the
smart pointer smaller and lighter. It seems to me that it's perfectly
possible to create a smart pointer class which works with incomplete
types without any overhead (except for that one static pointer per used
type, which is completely negligible).

I'm completely open to alternative techniques, but so far I haven't
seen anything that would meet both requirements, ie. working with
incomplete types and not burdening the user unnecessarily with
additional requirements.
 
K

Kai-Uwe Bux

Juha said:
I asked a long time ago in this group how to make a smart pointer
which works with incomplete types. I got this answer (only relevant
parts included):

//------------------------------------------------------------------
template<typename Data_t>
class SmartPointer
{
Data_t* data;
void(*deleterFunc)(Data_t*);

static void deleter(Data_t* d) { delete d; }

void decrementReferenceCount()
{
if(data)
{
// decrement reference count and then:
if(<reference count == 0>)
{
deleterFunc(data);
}
}
}

public:
SmartPointer(Data_t* d): data(d), deleterFunc(&deleter) {}
~SmartPointer() { decrementReferenceCount(); }
// etc...
};
//------------------------------------------------------------------

When done like this, the template type must be complete only when the
smart pointer is constructed. It doesn't need to be complete when it's
destructed, copied or assigned.

What bothered me back then is that the pointer to the deleter function
is a member variable of the class, increasing its size. What is worse,
all the smart pointer instances of the same type will have the exact
same function pointer as member variable, which feels like a huge waste.

Then it occurred to me: Is there any reason this pointer cannot be
static? Like this:

//------------------------------------------------------------------
template<typename Data_t>
class SmartPointer
{
...
static void(*deleterFunc)(Data_t*);
...
public:
SmartPointer(Data_t* d): data(d)
{
deleterFunc = &deleter;
}
...
};

template<typename Data_t>
void(*SmartPointer<Data_t>::deleterFunc)(Data_t*) = 0;
//------------------------------------------------------------------

This way the pointer to the deleter will be stored in the program only
once, and most importantly it will not increment the size of the smart
pointer.

The only thing I see is a problem with multi-threading. To be safe, I would
lock access to the deleter variable.


This feels so glaringly obvious to me now that I really wonder why
this wasn't suggested to me to begin with.

Because it is glaringly obvious :)

Is there some error here I'm missing?
[snip]

If so, I don't see it. I remember going down the same path at some point.
The code seemed to work fine. However, there is little need to beat
tr1::shared_ptr resource-wise.


Best

Kai-Uwe Bux
 
J

Juha Nieminen

Kai-Uwe Bux said:
The only thing I see is a problem with multi-threading. To be safe, I would
lock access to the deleter variable.

Of course, but that's a different issue. In this case I was wondering
about the space saving gained by this little technique.
Is there some error here I'm missing?
[snip]

If so, I don't see it. I remember going down the same path at some point.
The code seemed to work fine. However, there is little need to beat
tr1::shared_ptr resource-wise.

In most cases I suppose it doesn't matter how much memory the smart
pointer requires. However, there may be special cases where every byte
saved can make a big difference.
 
K

Kai-Uwe Bux

Juha said:
Of course, but that's a different issue. In this case I was wondering
about the space saving gained by this little technique.
[snip]

That would depend on two things: (a) the actual smart pointer implementation
and (b) the implementation of allocation. E.g., if allocation actually gets
you always multiples of 16 bytes (or 32) accounting for an overhead pointer
for memory management, you might end up not saving anything since a saving
of 4 bytes might not drop you below the next allocation threshold. Measure
and keep in mind that what works on one architecture may not be good on the
next.


Best

Kai-Uwe Bux
 
T

Thomas J. Gritzan

Juha said:
My original question was not really a "how do I make a smart pointer
which works with incomplete types?", but "does this work?"

My question was prompted from the realization that I could use the
same technique as boost::shared_ptr uses, but using a static function
pointer rather than a non-static one, and I was wondering why this
doesn't seem to be a common technique (in other words, maybe I have
missed some error in the code?)

Granted, by using a static function pointer it will not be possible to
define a deleter on a per-smartpointer basis, but that's not what I'm
worried about. I'm more worried about the space that pointer requires.
Making it static removes it from the smart pointer object, making the
smart pointer smaller and lighter. It seems to me that it's perfectly
possible to create a smart pointer class which works with incomplete
types without any overhead (except for that one static pointer per used
type, which is completely negligible).

shared_ptr doesn't store the deleter in the smart pointer object, but in
the struct where the reference counter is, together with the original
pointer passed in when the smart_ptr is constructed. Since you want to
store the class and the ref count together in one memory block, you
could do this:

template<typename T>
struct Holder
{
T object;
size_t refcount;
destroy_ptr<T> destroy;
};

This would only double the overhead the ref counter had.
 
T

Thomas J. Gritzan

Juha said:
My original question was not really a "how do I make a smart pointer
which works with incomplete types?", but "does this work?"

My question was prompted from the realization that I could use the
same technique as boost::shared_ptr uses, but using a static function
pointer rather than a non-static one, and I was wondering why this
doesn't seem to be a common technique (in other words, maybe I have
missed some error in the code?)

Granted, by using a static function pointer it will not be possible to
define a deleter on a per-smartpointer basis, but that's not what I'm
worried about. I'm more worried about the space that pointer requires.
Making it static removes it from the smart pointer object, making the
smart pointer smaller and lighter. It seems to me that it's perfectly
possible to create a smart pointer class which works with incomplete
types without any overhead (except for that one static pointer per used
type, which is completely negligible).

shared_ptr doesn't store the deleter in the smart pointer object, but in
the struct where the reference counter is, together with the original
pointer passed in when the smart_ptr is constructed. Since you want to
store the class and the ref count together in one memory block, you
could do this:

template<typename T>
struct Holder
{
T object;
size_t refcount;
destroy_ptr<T> destroy;
};

This would only double the overhead the ref counter had.
 
J

Juha Nieminen

Alf said:
I think I didn't grok what you meant here earlier.

For if you can arrange for complete type at construction, then it's
difficult for me to see how you can not just as easily arrange for
complete type at destruction, e.g., for PIMPL idiom, just give that
outer type a user defined destructor -- just an empty one.

This assumes that:

a) The smart pointer is used as a (non-static) member variable of some
class, and

b) The programmer will write a destructor for the class and implement
that destructor (even if it's just an empty implementation) in a context
where the type used by the smart pointer is complete.

If either a) or b) fails, the object might not be properly destroyed
(if the smart pointer does not support destroying object of incomplete
type).

The b) requirement is an additional requirement which is not needed.
It requires the user to first define the destructor of the outer class
in the header file and then implement it in the source code file (in a
context where the type used by the smart pointer is complete). Even in
the most minimum case it requires the user two write two additional
lines in two different files.

(Another minor point is that doing it like this makes it impossible
for the compiler to inline the destructor of the class, which might have
some minor efficiency impact in certain cases.)

Not a big deal? Well, the thing is that it's completely unnecessary to
make this requirement. It's perfectly possible to make the smart pointer
in such way that the user is not required to do this.

At the same time it will make the smart pointer usable (with
incomplete types) in other situations than as a member variable of a class.
To shave off one line of user code per class, for a relative rare case,
you think that approach with static pointer is a good idea?

It lessens the requirements of the smart pointer, making it easier to
use and more versatile. The technique has no drawbacks that I can think
of. So what would be the rational reason to not do it like that?
It might of course be complexity of that single line you're reacting to,
that it would be more difficult to understand than subtlety of smart
pointer class.

Then how about this line, always the same, no complexity, just copy n'
paste:

I'm sorry, but it just seems that you are struggling to avoid
admitting that you were wrong. (Ok, you indirectly admitted it by saying
that you "didn't understand" my original requirements (yeah, sure), but
you still are refusing to admit the soundness of the function pointer
technique, even though it's really not my invention, and used eg. by
boost::shared_ptr.)

Your proposed solution adds an unnecessary level of complexity to the
usage of the smart pointer and, what is worse, is error-prone: It
requires the user to select between different options depending on the
situation. All of this is completely unnecessary complexity because a
much simpler solution exists.
 
J

Juha Nieminen

Alf said:
You mean, by explaining things carefully, and even posting working code?

The first code you posted didn't work because it required the type to
be complete at destruction.

The second code might work but is unnecessarily complicated to use.
There's no need for such complication.

What you have failed to do is to explain exactly *why* the code in my
original post is "absurd". It's automatic, it's easy to use, it works
with incomplete types and it doesn't increment the size of the smart
pointer class. Exactly what is "absurd" there?

You are simply struggling around the problem of admitting, that
perhaps it's not as "absurd" after all.
 
J

Juha Nieminen

Thomas said:
shared_ptr doesn't store the deleter in the smart pointer object, but in
the struct where the reference counter is,

I know, but that doesn't make too much of a difference: It still
increments the memory requirements of shared_ptr by a pointer for each
managed object.

My idea was that by making the pointer static, then it increments the
memory requirements by a pointer for each *type* used, rather than for
each *object*.
together with the original
pointer passed in when the smart_ptr is constructed.

Actually no. The boost::shared_ptr class has two members: The pointer
to the managed object, and a pointer to the reference counter data
block. This is a direct copy-paste from the boost shared_ptr header:

T * px; // contained pointer
detail::shared_count pn; // reference counter

(detail::shared_count is a class which contains one pointer as member
variable.)
Since you want to
store the class and the ref count together in one memory block

I think you are confusing this thread with the other one, where I
wrote about my idea for the intrusive smart pointer (which works with
any type). My original question in this thread is not really related.
 
J

Juha Nieminen

Alf said:
No no no, both I and James explained this earlier: that's not what it does.

shared_ptr allows *different* deleters for different referents of same
type.

It's a very different design goal, and very different semantics.

No, it isn't. boost::shared_ptr is guaranteed to work with incomplete
types (as long as the type is complete at construction), and it does
*not* require the user to provide it with a deleter function. (The user
*can* provide one, but is not required to do so.)

That's *exactly* what those two requirements I listed above are. It's
exactly what I want.
(The only difference is that I am

You might look else-thread.

I don't know whether it's "better" in a general sense, but in the sense
of getting rid of your static pointer it certainly is.

You got rid of the static pointer by adding needless complexity to the
usage of the smart pointer. You have failed to explain why that would be
a better idea.

What do you have against the static function pointer? It doesn't
consume memory basically at all (one pointer per used type, that's all)
and it's not slower than your suggestion (because in both cases the
destructor calls a function indirectly through a function pointer). The
bonus is that it can be completely automatized, unlike your suggestion.
Anyways, it demonstrates you don't need that darned static pointer (or
any pointer) even for the case where you have incomplete type at
destruction.

Of course you don't need it if you are ok with making the usage of the
smart pointer more complicated. But why do that? There's no rational reason.
OK, you made me laugh. Heh. :) Thx.

Why do you have such a condescending attitude?
 
J

Juha Nieminen

Pete said:
shared_ptr<Base> ptr(new Derived);

ptr holds a Base*. Its control block holds a Derived*. Both point to the
same object. shared_ptr has to keep track of the actual type passed so
that it can delete the object correctly, among other things.

You mean shared_ptr will be able to properly destroy an object in that
case even if the destructor of Base is not virtual?
Different shared_ptr<T> objects can manage resources of different types
derived from T, with different requirements for deletion.

I understand that now. Basically shared_ptr can be given different
deleter functions for different objects, even if the type is the same.

While that might be useful in some rare cases, I still feel it breaks
the basic design principle of the C++ language: You don't have to pay
for what you don't use.

In this case you have to always pay the price of a per-object deleter
even if you don't use one.
 
J

Juha Nieminen

Kai-Uwe Bux said:
That would depend on two things: (a) the actual smart pointer implementation
and (b) the implementation of allocation. E.g., if allocation actually gets
you always multiples of 16 bytes (or 32) accounting for an overhead pointer
for memory management, you might end up not saving anything since a saving
of 4 bytes might not drop you below the next allocation threshold. Measure
and keep in mind that what works on one architecture may not be good on the
next.

That's true, of course. However, I have always looked for more
efficient ways of doing things (without compromising usability and safety).

If you make, for example, a big vector of smart pointers (with maybe
millions of them), the optimal situation is if your smart pointer has
the size of one single pointer (or if that's just not possible, to be as
small as possible). In this case every saved byte in the smart pointer
counts.

Also how the reference counting is implemented can have a big impact.
For example, if you use actual integers (allocated separetely) to count
the references, using a reference counter pool (which allocates only
sizeof(size_t) bytes of memory per counter), that can have an enormous
beneficial impact in memory usage if the amount of smart pointer
instances is very large.

In this situation you can make the smart pointer support incomplete
types for free: This support doesn't require any additional memory per
smart pointer instance. This can be an enormous advantage in some
situations.
 
T

Thomas J. Gritzan

Juha said:
I know, but that doesn't make too much of a difference: It still
increments the memory requirements of shared_ptr by a pointer for each
managed object.

My idea was that by making the pointer static, then it increments the
memory requirements by a pointer for each *type* used, rather than for
each *object*.

That works if the type of the managed object _always_ is the same as
template parameter of the smart pointer object. Consider this:

shared_ptr<Derived> pd = shared_ptr<Derived>(new Derived);
shared_ptr<Base> pb = pd;
shared_ptr<void> pvoid = pb;

Any of these have to be able to delete the object using the correct
deleter. So each smart pointer object holds a pointer to
a) the object it is referring to, using the correct type (i.e. void* for
the last)
b) a pointer to the control object

The control object holds
a) the reference count
b) a counter counting all strong _and_ weak counts
c) a pointer to the deleter function
d) the _original pointer_ used in constructing the first smart pointer
e) a mutex, additional stuff, etc.

The original pointer is passed to the deleter function. If the smart
pointer pvoid above has to delete the derived object, the smart pointer
object only has a void*. With a void*, you cannot destroy a Derived. So
shared_ptr has to remember the original pointer used to construct it.

Following this link, some programming techniques are described which
partly rely on having a separate deleter for every (non-shared)
shared_ptr instance:
http://www.boost.org/doc/libs/1_36_0/libs/smart_ptr/sp_techniques.html
Actually no. The boost::shared_ptr class has two members: The pointer
to the managed object, and a pointer to the reference counter data
block. This is a direct copy-paste from the boost shared_ptr header:

T * px; // contained pointer
detail::shared_count pn; // reference counter

(detail::shared_count is a class which contains one pointer as member
variable.)

The pointer object (i.e. each instance of shared_ptr) only has these,
but the original pointer is stored in the control object. See above.

[about Juha Nieminen's smart pointer]
I think you are confusing this thread with the other one, where I
wrote about my idea for the intrusive smart pointer (which works with
any type). My original question in this thread is not really related.

I know this is another thread, but I thought you asked for a technique
for your smart pointer.

By the way, your wording is confusing. Your SP is not intrusive since
you don't have to change the managed class. It is non-intrusive like
shared_ptr but it allocates the control object together with the managed
class.
 
J

Juha Nieminen

Alf said:
* Juha Nieminen:

I beg your pardon, it worked very nicely. I posted tested code to you.
Don't lie.

You posted a smart pointer which only works for incomplete types if
the user provides the smart pointer class with a pointer to a deleter
function, and then you wrote an example of how to use it like that.

However, your code does not work for incomplete types if the user does
not give it the deleter function. Thus it doesn't work for all cases.
It did not have such requirement.

In your original code you directly called the templated deleter
function from the destructor of the smart pointer. That instantiates the
deleter function in the context where the destructor is called and thus
requires for the type to be complete.
Perhaps you managed to misunderstand it.

I understood that in order to work with incomplete types the user has
to give it a deleter function. Too burdensome and unnecessary.
Note that in all codes so far, including your own, there has been a
function that does the destruction, at a point where the type is
complete; I'm assuming that is not what you're referring to, since that
would be a fallacy.

The difference is whether the type must be complete when the
destructor (or assignment operator) of the smart pointer is executed or
not. The destructor must not instantiate the deleter function (because
if it does so, it immediately requires for the type to be complete). The
only way to avoid that is to call the deleter function indirectly
through a pointer.
It's dead easy to use, but it's not as good a solution as the first.

Both of your solutions are worse than the one used by
boost::shared_ptr and my variant because both of your solutions require
additional things from the user which eg. boost::shared_ptr doesn't.
I don't know what you think is absurd, but the static pointer in there
is, as I wrote earlier, nonsensical. A very dirty trick to make the
client code less explicit, and less maintainable (think about replacing
your smart pointer class with something more general or just different),
and less thread-safe, in a very rare case.

Of course it makes the user code less explicit. That's the whole point
of abstraction. Ever heard of such a thing? Abstraction adds to the
maintainability and reusability of the code, not the other way around.

As for thread-safety, your solutions are in no way better. The user
manually providing a deleter function does not make it any more thread-safe.

Besides, all your complaints apply directly to boost::shared_ptr
because it uses THAT EXACT SAME MECHANISM as I did in my original post.
For some reason you don't seem to get this simple fact.
All in order to avoid 1 line of client code in that rare case.

Why require any additional lines for no good reason? boost::shared_ptr
doesn't require any such additional lines, and it works with incomplete
types just fine.
From my point of view it is a dirty hack.

Used by boost::shared_ptr.
But it wasn't your hack. You
asked how to avoid it

Excuse me? Where exactly did I ask this?

What I asked was whether the function pointer member variable could be
made static or not.
and I've shown you some ways

Which only make the usage of the smart pointer more complicated with
no benefit whatsoever.
, and instead of
whining and worrying (but I notice that that wasn't until after an
anonymous attack, don't let those trolls influence you) I'd expect
thanks for giving you solutions to stated prob.

I'd give thanks if your solutions worked, but they don't. They just
make the usage of the class more complicated for no reason.
 
J

James Kanze

You mean shared_ptr will be able to properly destroy an object
in that case even if the destructor of Base is not virtual?

Yes, provided it was originally constructed with a pointer to
derived. E.g.:
boost::shared_ptr< Base > p( new Derived ) ;
is fine, but
Base* tmp = new Derived ;
I understand that now. Basically shared_ptr can be given
different deleter functions for different objects, even if the
type is the same.
Exactly.

While that might be useful in some rare cases, I still feel it
breaks the basic design principle of the C++ language: You
don't have to pay for what you don't use.

I find it relatively rare to use a boost::shared_ptr without a
custom deleter. And the cost is very close to zero, once the
design requirement is established that it must work with
"unco-operative" types (i.e. classes which don't provide the
counter as a member somehow).
In this case you have to always pay the price of a per-object
deleter even if you don't use one.

And what is that price?
 
J

Juha Nieminen

Alf said:
* Juha Nieminen:

No, shared_ptr does not use a static pointer.

It uses a regular pointer, which is even worse.

Your whole point was that storing this pointer anywhere could be
avoided altogether.
There's no error-prone-ness: you can't do wrong.

So an additional requirement of "if you instantiate the smart pointer
in an environment where the type is complete, include this file, else
include this another file" is not error-prone?

With the boost::shared_ptr solution there's only one single include
file and no possibility of error.
What complexity.

Having to choose between different header files depending on whether
the type might be incomplete or not.
The simplest is to do exactly what you want to achieve,
telling the compiler exactly what that is.

That's not the simplest thing to do. The simplest thing is when you
*don't have to* tell the compiler about your special cases and instead
the compiler can deduce them itself automatically.

If having to do everything explicitly is the "simplest" thing to do,
then you should switch to C and forget about C++. There you won't have
to worry about the compiler or the libraries doing things for you
automatically.
Instead of introducing
roundabout detours via static pointers, which *is* complexity.

We are talking about complexity from the point of view of the user, in
other words, the complexity of the public interface of the class and its
requirements, ie. how complicated it is to use.

How complicated the private implementation of a class is, is
completely inconsequential as long as it causes the public interface to
be easy to use and safe, and preferably the class as efficient as possible.

From the computer's point of view one function pointer per used type
is nothing. It doesn't even require any significant amounts of memory.
Thus the implementation is very efficient with respect to memory
consumption.

Adding requirements to the public interface of the class (eg.
requiring the user to write additional lines) when there's no functional
or significant efficiency reasons to do so simply doesn't make sense.
I have trouble grasping why you're arguing for that pointer, given that
your question was how to get rid of it.

Where did you get this impression? I never said I want to get rid of
it. What I said is that I want to take it out of the smart pointer
object so that it doesn't increase its size.

Of course getting completely rid of it (without making the usage of
the smart pointer more complicated) would be cool, but it's technically
impossible. The simple fact is: If the object must be deletable by the
smart pointer in a context where the type is incomplete, the destructor
of the smart pointer cannot delete it directly, nor can it instantiate
any deleter function which does so.

This goal can be achieved by making the user create such a function
and giving it to the smart pointer. No matter how many tricks you try to
invent to automatize this as much as possible, it will always require
the user to explicitly write something extra. This is needlessly
burdensome and error-prone. It's needlessly so because it can be easily
avoided.

Another way of achieving this is by the smart pointer automatically
instantiating the deleter function itself. The only place where it can
do so (as per the specs) is in the constructor. After this the only way
the destructor can call this automatically generated function without
actually instantiating it itself as well, is through a function pointer.
There's no way of passing this function pointer from the constructor to
the destructor other than storing it somewhere.

This is the exact technique that boost::shared_ptr uses to
automatically support incomplete types.
 
J

Juha Nieminen

Thomas said:
I know this is another thread, but I thought you asked for a technique
for your smart pointer.

In fact, I originally wasn't thinking about my intrusive smart pointer
when I made the original post, but this discussion got me thinking about
how I could make it support incomplete types (preferably without too
much overhead), and I have got a few good ideas. It may well be possible
to make it support incomplete types almost for free (except that the
deleter must be called indirectly when the object is destroyed, but
that's probably negligible given that object deletion itself is a
relatively heavy operation).
By the way, your wording is confusing. Your SP is not intrusive since
you don't have to change the managed class. It is non-intrusive like
shared_ptr but it allocates the control object together with the managed
class.

It may be that using "intrusive" in the name of my RefPtr class might
not be technically completely correct. However, for almost all intents
and purposes it behaves like a classical intrusive smart pointer: The
size of the smart pointer class itself is that of one single pointer,
and the reference count is in the same memory block as the managed
object rather than being allocated separately, and thus it has all the
efficiency advantages of a classical intrusive smart pointer.

I can't think of a more descriptive term for this...
 
J

Juha Nieminen

Alf said:
The "No, it isn't" is incorrect.

The rest is correct, but unrelated.

Unrelated? So boost::shared_ptr just happens by lucky chance to
support incomplete types exactly the way I have been describing, even
though that's not the goal of boost::shared_ptr?
You have failed to explain what the alleged "complexity" consists of.

The additional requirements for the usage of the smart pointer in your
two different solutions.

boost::shared_ptr and my variant do not need *any* additional
requirements whatsoever from the user, compared to a smart pointer
implementation which does not support incomplete types. They can be used
*exactly* as you would use a simple vanilla smart pointer which works
only for complete types.

Having to write even one single additional line compared to this is
useless complexity and is not needed.
You have failed to explain why you think the static pointer is a good
idea.

Because it doesn't change the public interface nor the usage of the
smart pointer in any way, compared to a basic smart pointer. Everything
happens automatically and opaquely.

Your solutions add extra requirements for the user. For no good reason.
You started by asking how to get rid of it

Where exactly?
and that's
inconsistent with present position, which begs the question, why the
change of heart?

You might have a point if I had asked how to get rid of the function
pointer, but I haven't.

The only thing I have asked is why the function pointer could not be
static rather than non-static.
 

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

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top