auto_ptr

A

Andre Kostur

Grizlyk said:
1.
Can be const, of course. The "const state" does not mean "const each
member" and appearence in C++ keyword "mutable" just proves it.

You also have const_cast too. But that doesn't mean that you should use
it in that manner. Mutable has its uses. It's not intended to allow you
to violate the "semantic constness" of the object. Remember, C++ tends
to try to protect programmers from accident, not maliciousness. You can
break C++'s type safety in many, many ways. The designers had realized
that there are certain cases where the object may be const, but certain
members may need to be modified, even on const objects. Let's assume we
have some sort of object that when a certain const member is called, it
causes a complex calculation to occur. Perhaps this class can take
advantage of caching the result so that future calls to that member
(without any other intervening non-const calls) could return the cached
value instead of having to recalculate it. This is a good candidate for
mutable. The cached value being populated isn't changing the semantic
state the object.
Consider

my_auto_ptr<Tobj> a(new Tobj); //1
my_auto_ptr<Tobj> b(a); //2

At the point 2 we are moving "new Tobj" to "b". Is it C-style pointer
behaviour? I think not. But fortunately my_auto_ptr<Tobj> is not
pointer, so we must not duty to think, that the moving "new Tobj" from
"a" violates "a" constness.

No const in sight there. However, given:

const my_auto_ptr<Tobj> a(new Tobj);
my_auto_ptr<Tobj> b(a);

I would be very surprised to find out that a now no longer holds the data
that I had given it.
You just must not expect from my_auto_ptr<Tobj> any return, because it
is not pointer, it is special memory wrapper with complex behaviour.

The goal of my_auto_ptr<Tobj>
1. to hold dynamic data for parameter of function
2. to delete the data on return from the function (if the data was
not
used by the function) or to pass the data to user

That is why operation move() is just a kind of operation, that must be
applyed to my_auto_ptr<Tobj> only once insted to be non-const!

The problem here is that operation move() isn't a logically const
operation. It changes the "semantic" state of the object.
You can not protect yourself from double move() with the help of
non-const modifier, but as opposite, const my_auto_ptr<Tobj> can
protect your implementation from wrong assignment to
my_auto_ptr<Tobj>.

Consider

void foo(const my_auto_ptr<Tobj> ptr)
{
Tobj *const a=ptr.move(); //ok
Tobj *const b=ptr.move(); //error, const do not protect
ptr=new Tobj; //error, but const will protect
}

Um, you're passing an object by const value. The specific argument I'm
attempting to make:


// Function somewhere else. Contents unknown,
// function is defined in a different translation
// unit.
void fn(const my_auto_ptr<Tobj> & aptr);

{
my_auto_ptr<Tobj> ptr(new Tobj);

fn(ptr);

ptr->DoSomethingOnPtr();
}

I would expect that to have useful behaviour, and specifically, I
wouldn't expect the pointer to become NULL.
 
G

Grizlyk

Andre said:
You also have const_cast too. But that doesn't mean that you should use
it in that manner. Mutable has its uses. It's not intended to allow you
to violate the "semantic constness" of the object.

You are right about constness, but not right for concrete my_auto_ptr<Tobj>,
because "semantic constness" of my_auto_ptr<Tobj> is not the same as
"semantic constness" of a C-style pointer, because my_auto_ptr<Tobj> is not
a C-style pointer. However, in some limited cases the behaviour of
my_auto_ptr said:
No const in sight there. However, given:

const my_auto_ptr<Tobj> a(new Tobj);
my_auto_ptr<Tobj> b(a);

Yes, even both are const

const my_auto_ptr<Tobj> a(new Tobj);
const my_auto_ptr said:
I would be very surprised to find out that a now no longer holds the data
that I had given it.

You must not be surprised, because "b(a)" is really "once" operation
(operation that can be applied to "my_auto_ptr<Tobj>" only once) instead of
"const" operation. And you must _not_ do like this

{
const my_auto_ptr<Tobj> a(new Tobj);
const my_auto_ptr<Tobj> b(a);
a.get_pointer(); //error
}

There are many other cases, when we must keep strict sequence of operations
applied to, for example

{
const char *ptr="test";
ptr=0;
strlen(ptr);//runtime error - access to memory NULL
}

Aby _auto_ ponter passed by const-ref is nonsence, really, has no reasons to
do it, if you no need to control memory allocation at the point just pass
ordinary C-style pointer instead of _auto_ ponter, external code, where
_auto_ ponter was created, will control memory allocation.

Note, _auto_ ponter passed by value gives no overhead in comparison with
_auto_ ponter passed by const-ref.
The problem here is that operation move() isn't a logically const
operation. It changes the "semantic" state of the object.

I have said about my_auto_ptr said:
Um, you're passing an object by const value. The specific argument I'm
attempting to make:

// Function somewhere else. Contents unknown,
// function is defined in a different translation
// unit.
void fn(const my_auto_ptr<Tobj> & aptr);

error said:
{
my_auto_ptr<Tobj> ptr(new Tobj);

fn(ptr);

ptr->DoSomethingOnPtr();

error you must not reuse my_auto_ptr<Tobj> after fn(ptr) due to
my_auto_ptr said:

Returning to "once" usage.
It is evidently, we can make runtime tests of my_auto_ptr<Tobj> usage, but
we want compile time testing.

I think twice about "once", and see, that it is not enough. Assuming
my_auto_ptr<Tobj> has in addition member get_ptr:

//!not C++
Tobj* my_auto_ptr<Tobj>::get_ptr()const {return ptr;}
Tobj* my_auto_ptr<Tobj>::move()const once
{
Tobj *const tmp=ptr;
ptr=0;
return tmp;
}

const my_auto_ptr<Tobj> a(new Tobj);
a.move();
a.move(); //ok - error detected
a.get_ptr(); //wrong - error _not_ detected

I think, we need to tell to compiler that
- if "move()" has executed, "get_ptr()" must not be executed,
- if "move()" has not executed, "get_ptr()" can be executed by conditions of
get_ptr "oneceness".

Let's think, that if "move" has executed, it looks like my_auto_ptr<Tobj>
was logically deleted, no one "returning state" operation can be aplied,
only assignment or destructor.

In fact "move" logically is desructor with name and can return value. So we
need instead of "once" to mark "move" as "dtor", "get_ptr" as "returning
state" and "replace" as "ctor", assuming "returning state" as default value
we will get:

//!not C++
Tobj auto*
my_auto_ptr<Tobj>::get_ptr()const
{return ptr;}

//can "const dtor" pair be applied ot not?
//i think yes, because dtor can destroy const objects
Tobj heap*
my_auto_ptr<Tobj>::move()const dtor
{
Tobj heap *const tmp=ptr;
ptr=0;
return tmp;
}


//can "const ctor" pair be applied ot not?
//i think no, because ctor can not re-create const objects
my_auto_ptr<Tobj>&
my_auto_ptr<Tobj>::
replace(Tobj heap *const new_obj)ctor
{
if(ptr)delete ptr;
ptr=new_obj;
return *this;
}

Now nearly exelent:

{
const my_auto_ptr<Tobj> a(new Tobj);
a.get_ptr(); //ok - get
a.get_ptr(); //ok - get
delete a.get_ptr(); //ok - compile time error detected
delete a.move(); //ok - destroy
a.move(); //ok - compile time error detected
a.get_ptr(); //ok - compile time error detected
a.replace(new Tobj); //ok - compile time error detected

const my_auto_ptr<Tobj> b(new Tobj);
b.replace(new Tobj); //ok - new value
b.replace(new Tobj); //ok - new value
b.move(); //ok - destroy
b.move(); //ok - compile time error detected
b.get_ptr(); //ok - compile time error detected
b.replace(new Tobj); //ok - new value

Tobj tmp;
b.replace(&tmp); //ok - compile time error detected
}

But exist new problem - we can try extract unassigned value

my_auto_ptr<Tobj> a;
a.get_ptr(); //wrong - error _not_ detected

We must tell to compiler, that a variable must be assigned befor usage, but
we can not have "unassigned values" after default ctors. For example, we can
create functions to user access and mark the function sequence calls

protected:
void
my_auto_ptr<Tobj>::
set_ptr_value
(Tobj heap *const new_adr)
{ptr=new_adr;}

Tobj auto*
my_auto_ptr<Tobj>::
get_ptr_value()
const
after set_ptr_value
{return ptr;}

public:
Tobj auto*
my_auto_ptr<Tobj>::get_ptr()const
{return get_ptr_value();}

//"ctor" and "dtor" reset "after" sequence
my_auto_ptr<Tobj>&
my_auto_ptr<Tobj>::
replace(Tobj heap *const new_obj)
ctor
{
if(ptr)delete ptr;
ptr=set_ptr_value(new_obj);
return *this;
}

well, now much beter
my_auto_ptr<Tobj> a;
a.get_ptr(); //ok - compile time error detected
a.replace(new Tobj); //ok - new value
a.get_ptr(); //ok - get
 
R

Rolf Magnus

Grizlyk said:
1.
Can be const, of course.

Should definitely be.
The "const state" does not mean "const each member" and appearence in C++
keyword "mutable" just proves it.

Right. But the mere existance of the mutable keyword doesn't mean you _have_
to use it.
Consider

my_auto_ptr<Tobj> a(new Tobj); //1
my_auto_ptr<Tobj> b(a); //2

At the point 2 we are moving "new Tobj" to "b".

Yes, and it changes both a and b.
Is it C-style pointer behaviour? I think not.

That's right. If it would be have like a C-style pointer, making the copy
constructor's argument const would be just the right thing to do.
But fortunately my_auto_ptr<Tobj> is not pointer, so we must not duty to
think, that the moving "new Tobj" from "a" violates "a" constness.

Well, there is a concept often called "logical constness", which is more a
design than a language concept. It means that if the object as it appears
to its user is changed by one of its member functions, that member function
should not be const. The mutable keyword is only for internal state that
gets changed without the object's user noticing (an example would be some
cache to speed up multiple reads from a slow source).
You just must not expect from my_auto_ptr<Tobj> any return, because it is
not pointer, it is special memory wrapper with complex behaviour.

Yes, but that behavior should not go on behind my back. If something is
const, I assume it doesn't change. That's why it's const in the first
place. If you make classes that are changed in const member function, you
basically render the whole idea of constness useless.
The goal of my_auto_ptr<Tobj>
1. to hold dynamic data for parameter of function
2. to delete the data on return from the function (if the data was not
used by the function) or to pass the data to user

Then you don't need to go through all the dynamic allocation and auto_ptr
stuff. Just make a local varible in the caller, and give a normal pointer
(or reference) to that to the function. auto_ptr doesn't give you any
advantage over that. Even if you must use dynamic allocation, use an
auto_ptr in the caller, but just pass a regular pointer to the function you
are calling. There is no need to pass an auto_ptr.
That is why operation move() is just a kind of operation, that must be
applyed to my_auto_ptr<Tobj> only once insted to be non-const!

Not a good idea.
You can not protect yourself from double move() with the help of non-const
modifier, but as opposite, const my_auto_ptr<Tobj> can protect your
implementation from wrong assignment to my_auto_ptr<Tobj>.

Your move() function changes the object just like the assignment operator
does. It seems inconsequent to make one of them const and the other not.
Consider

void foo(const my_auto_ptr<Tobj> ptr)
{
Tobj *const a=ptr.move(); //ok
Tobj *const b=ptr.move(); //error, const do not protect
ptr=new Tobj; //error, but const will protect
}

void foo(my_auto_ptr<Tobj> ptr)
{
Tobj *const a=ptr.move(); //ok
Tobj *const b=ptr.move(); //error, non-const do not protect
ptr=new Tobj; //error, non-const do not protect
}

How about simply not spending it an assignment operator that takes a regular
pointer as argument?
 
A

Andre Kostur

Grizlyk said:
You are right about constness, but not right for concrete
my_auto_ptr<Tobj>, because "semantic constness" of my_auto_ptr<Tobj>
is not the same as "semantic constness" of a C-style pointer, because
my_auto_ptr<Tobj> is not a C-style pointer. However, in some limited
cases the behaviour of my_auto_ptr<Tobj> can looks like behaviour of
C-style pointer, else we can not use my_auto_ptr<Tobj> to hold memory
adsress.

You are still violating the "semantic constness" of the object. Or at
least as it is commonly understood. You are far better off not using
const anywhere (and with no const-methods, makes a const instance of your
class unusable anyway), and enforcing your rules in program logic. Far
better than surprising _every_ potential user of the class by having
"const" instances which can change.

Upon copy-constructing the object, you are very significantly changing
the state of the object. Offhand I can't think of any object where after
calling a const method, suddenly there's a whole bunch of other methods
that I am now no longer allowed to call.

If one insists on designing a class in such a manner, I know I wouldn't
ever use that class.
Yes, even both are const

const my_auto_ptr<Tobj> a(new Tobj);


You must not be surprised, because "b(a)" is really "once" operation
(operation that can be applied to "my_auto_ptr<Tobj>" only once)
instead of "const" operation. And you must _not_ do like this

It doesn't matter how many times you can call it. The first time is bad
enough. If it's const, it must not change. If it doesn, I can no longer
trust the const mechanisms of the language. I will not use such a class.
{
const my_auto_ptr<Tobj> a(new Tobj);
const my_auto_ptr<Tobj> b(a);
a.get_pointer(); //error
}

That's simple (assuming you drop the consts). The copy constructor of b
also sets a boolean in a as to whether it currently holds a "valid"
pointer. When get_pointer is called, that variable is checked, and if a
doesn't hold a "valid" pointer, throw an exception.
There are many other cases, when we must keep strict sequence of
operations applied to, for example

{
const char *ptr="test";
ptr=0;
strlen(ptr);//runtime error - access to memory NULL
}

This is already covered as a precondition of strlen, such that strlen
only takes C-style strings. A NULL pointer is not a C-style string, thus
the programmer is improperly using his tools.
Aby _auto_ ponter passed by const-ref is nonsence, really, has no
reasons to do it, if you no need to control memory allocation at the
point just pass ordinary C-style pointer instead of _auto_ ponter,
external code, where _auto_ ponter was created, will control memory
allocation.

Sure... but is there any reason to actively prevent someone from passing
around a const-ref to it? Under normal circumstances, there's no problem
(beyond requiring users to pass in a smart pointer, naked pointers aren't
allowed). The problem with your description of your class, a const
version of it isn't really const. It might change.
Note, _auto_ ponter passed by value gives no overhead in comparison
with _auto_ ponter passed by const-ref.

Yes it does.. the auto_ptr by value needs to transfer ownership of the
pointer into the function parameter. Passing by const-ref does not.
error, do not pass my_auto_ptr<Tobj> by ref

Again, another thing that will make me not use a class desgined in such a
manner. The only way that this makes any sense is if the class doesn't
have any const methods, thus there's nothing useful that I can do with a
const instance of the class.
error you must not reuse my_auto_ptr<Tobj> after fn(ptr) due to
my_auto_ptr<Tobj> logic.

That's the problem with your class. You don't know whether you can do
anything with ptr or not. You have the potential that fn() might have
changed the contents of ptr. You don't know. You don't have access to
fn()'s implementation. Even if the documentation for fn() mentions that
it _might_ move the contents of ptr, you still don't know if ptr is valid
or not upon return of fn(). You've made the const system useless within
the context of this class. I'm not going to use it.
Returning to "once" usage.

You can stick a boolean into your class to record whether it is in some
sort of valid state or not. In any method that cares, it can check
and/or set that variable, and throw exceptions where necessary to enforce
it.


Say... here's another usage thought. Given your class, how can I express
that I have an instance of the class such that one is never allowed to
transfer ownership away from it? Normally I would:

const auto_ptr ptr(new Obj);

This would be sufficient to prevent constructs such as:

auto_ptr ptr2(ptr); // Fails at compile time
// ptr2 needs a non-const ref to work

However, if we replaced auto_ptr with your my_auto_ptr, this construct
would compile perfectly fine.
 
G

Grizlyk

Andre Kostur wroe:
You are still violating the "semantic constness" of the object. Or at
least as it is commonly understood. You are far better off not using
const anywhere (and with no const-methods, makes a const instance of your
class unusable anyway), and enforcing your rules in program logic. Far
better than surprising _every_ potential user of the class by having
"const" instances which can change.

Try to write here strict defininiton of "semantic constness" of any object
(as you understanding the definition) and it can help you to see my opinion.

I can say, that your words "You are still violating the semantic constness"
just showing that you are assuming without any provements (without becouse
you do not say short and clear why do you think, that your definition of
"object state" is better than all other posible definitions of "object
state") that "const for class means total const of all member or you are
That's simple (assuming you drop the consts). The copy constructor of b
also sets a boolean in a as to whether it currently holds a "valid"
pointer. When get_pointer is called, that variable is checked, and if a
doesn't hold a "valid" pointer, throw an exception.

It is evidently, that we always can make run-time testing, but we no need
it, we need compile time testing and speaking about last.
Sure... but is there any reason to actively prevent someone from passing
around a const-ref to it?

From one side, programmer (who is declaring function interface) must
understand what he is going to do and must not pass large structures by
value, declare NULL as default value of address of buffer to write or pass
auto_ptr by reference.

From other side, compiler must prevents all errors, that can be prevented.
And compiler can issue warnings on wrong usage of any methods with "once",
"ctor", "dtor" and "after" modifiers of any class. Consider

// ! not C++
void boo(const my_auto_ptr<Tobj>&);
char woo(const my_auto_ptr<Tobj>& ptr){ return (*ptr)? 1: 0; }
Tobj heap* my_auto_ptr<Tobj>::move()const dtor;

void foo(const my_auto_ptr<Tobj> ptr)
{
if:):is_ok){ptr.move();}

//warning - possible double "move()const dtor;"
//if "::is_ok" is not synced with "lib::all_ok()"
if(!lib::all_ok()){ptr.move();}

//warning - compiler lost control for
//"ptr having "once", "ctor", "dtor" or "after" modifiers"
//because body of boo is unknown
boo(ptr);

//ok - compiler know what are you doing
woo(ptr);
}

I think C++ must either define internal set of smart pointers (as auto_ptr)
with reliable complex behaviour, or define tools (working at compile time)
of language to allow users to make own reliable ordinary classes as smart
pointers.

Your method of avoiding compiler's problems with the help of declaring
objects as "non-const" (for non-const we are not expect any) is not good,
because it is just ignoring necessary static tests, as void declaration is
ignoring static check of type.

By reasons of my_auto_ptr<Tobj> logic, "non-const" is needed only if you are
going to assign new pointer value after my_auto_ptr<Tobj> has created.
Operation "move" is in dact destructor and destructor can be applied to
const object also, so "move" must be const.
Again, another thing that will make me not use a class desgined in such a
manner. The only way that this makes any sense is if the class doesn't
have any const methods, thus there's nothing useful that I can do with a
const instance of the class.

You are rignt, technically you can not use object passed by reference if the
class of the object doesn't have any const methods.

But logically you sometimes also can not use object passed by const
reference, even if the class of the object have some const methods, and
class my_auto_ptr<Tobj> is example of this.

It is easy to see, that even so simple class as my_auto_ptr<Tobj> need for
special support from compiler (if we want remove extra runtime testing).
Declaring methods of the class as const or non-const is not enough!

With the help of "once", "ctor", "dtor" and "after" modifiers of methods of
class we can remove runtime if-else operators for conditions linked with
"object state" and can remove extra data fields keeping the flags of "object
state" and can make testing of the if-else of the conditions at compile
time.

With the help of the modifiers we can declare not only set of members of
interface, but in addition can declare sequence of calling the methods of
the interface.

Without them it is just as (...) or void - no check at compile time.

That's the problem with your class. You don't know whether you can do
anything with ptr or not. You have the potential that fn() might have
changed the contents of ptr.

If you want to return ptr value, you can do like this

my_auto_ptr<Tobj> fn(const my_auto_ptr<Tobj>);
ptr=fn(ptr);

You don't have access to fn()'s implementation.

Again - do not pass ptr to fn by reference. And you no need access to fn()'s
implementation. For class with explicit "once", "ctor", "dtor" and "after"
modifiers compiler can print warning that control of object state lost due
to reference.

You are habit, that passing by value no always possible, but there are
cases, when passing by reference is impossible too.
You can stick a boolean into your class to record whether it is in some
sort of valid state or not. In any method that cares, it can check
and/or set that variable, and throw exceptions where necessary to enforce
it.

See upper - we need compile time checking, not runtime for so simple class
as auto_ptr.

But i am thinking at last of the letter, maybe C++ must allow to declare
class as disabled to pass the object of the class by reference?

We can disable copy (pass by value) declaring copy ctor as protected. Maybe
expression "class_name&" or "const class_name&" can be treated as operator
and the operator can be declared as protected?

And do we need "befor" keyword, if we need "after"?
 
G

Grizlyk

Rolf said:
That's right. If it would be have like a C-style pointer, making the copy
constructor's argument const would be just the right thing to do.


Well, there is a concept often called "logical constness", which is more a
design than a language concept. It means that if the object as it appears
to its user is changed by one of its member functions, that member
function
should not be const. The mutable keyword is only for internal state that
gets changed without the object's user noticing (an example would be some
cache to speed up multiple reads from a slow source).

The address of object stored to auto_ptr is true internal state (the address
is private memeber), because after "move" external must not try to do any
with the address of object stored to auto_ptr, because "move" is in fact
"destructor". But C++ do not allow to declare the fact and you are trying to
use non-cost in the hope, that it will help "expect nothing" from class. It
is not true, because "non-const" can not help you to disable double "move"
or disable use pointer stored in auto_ptr after "move".

The "move" in fact is "destructor", but destructor must be able to destroy
const objects, so "move" must be const.

I am sure, that even appearence of "const" has been met by programmers
negatively. But at least for support such easy classes as auto_ptr C++ must
declare "once","ctor", "dtor" and "after" modifiers not only "const".
Yes, but that behavior should not go on behind my back. If something is
const, I assume it doesn't change. That's why it's const in the first
place. If you make classes that are changed in const member function, you
basically render the whole idea of constness useless.

Not sure hat you are right, all depending on what is "object state" and
"change object state".
Then you don't need to go through all the dynamic allocation and auto_ptr
stuff. Just make a local varible in the caller, and give a normal pointer
(or reference) to that to the function. auto_ptr doesn't give you any
advantage over that. Even if you must use dynamic allocation, use an
auto_ptr in the caller, but just pass a regular pointer to the function
you
are calling. There is no need to pass an auto_ptr.


Not a good idea.

Why? Are you going to move twice?
Your move() function changes the object just like the assignment operator
does. It seems inconsequent to make one of them const and the other not.

Not of course, because "move" does not look like "=", we are not writing
like this

int a.move(3);

so if you are trying to use not assign but "move" - see "move" declaration
and comments.
How about simply not spending it an assignment operator that takes a
regular
pointer as argument?

Do not understand.
 
A

Andre Kostur

Grizlyk said:
Andre Kostur wroe:

Try to write here strict defininiton of "semantic constness" of any
object (as you understanding the definition) and it can help you to
see my opinion.

A strict definition is pretty hard to nail down as there are too many
edge cases. That all depends on the intent of the class. As an example,
the intent of the auto_ptr class is to hold a pointer, and ensure that it
is deleted at the end of the life of the auto_ptr. The "state" of the
auto_ptr would be the contained pointer. Since copying an auto_ptr
involves transferring the ownership of the pointer to the new auto_ptr,
this constitutes a change in state of the originating auto_ptr, namely
that it is no longer responsible for deleting that pointer (and according
to standard, has it's contained pointer changed to NULL). As a result,
you cannot assign a const auto_ptr to anything, as to do so it would
change the state of the auto_ptr.

I can say, that your words "You are still violating the semantic
constness" just showing that you are assuming without any provements
(without becouse you do not say short and clear why do you think, that
your definition of "object state" is better than all other posible
definitions of "object state") that "const for class means total const
of all member or you are thinking that my_auto_ptr<Tobj> is C-style
pointer", but both opinions are wrong.

Note that a const instance does not necessarily mean that all of the
members must be const. As you have mentioned, see the mutable keyword.
However it is a tool that must be used correctly. In the auto_ptr
example, it would be wrong to make the contained pointer mutable as it
contributes directly to the purpose of the class. If we were to look at
the std::string object, recall that c_str() returns a char pointer to a
C-style string that is correctly 0-terminated. Let's assume that the
internal representation of the string doesn't have a 0-terminated buffer.
So when you call c_str(), the string needs to reassemble its internal
buffer into a new buffer that is returned by c_str(). However, it could
store this buffer pointer into a "cache" member variable so that if a
user were to call c_str() many times in a row, the string class could
simply return this cached pointer (note that c_str() already defines that
the char * returned by it is only good until you call a non-const method
on that string). This cached pointer could be declared as a mutable
member since it does not contribute directly to the state of the string
object.
It is evidently, that we always can make run-time testing, but we no
need it, we need compile time testing and speaking about last.

Offhand I don't see how you could enforce your "once" keyword across
translation units at compile time. How would the system know that:

my_auto_ptr<Obj> ptr(new Obj);

fn(ptr);

ptr->get_pointer();

is legal or not? Remember that all the compiler knows is that fn() takes
a my_auto_ptr as a parameter, it doesn't know what its going to do with
it.

From one side, programmer (who is declaring function interface) must
understand what he is going to do and must not pass large structures
by value, declare NULL as default value of address of buffer to write
or pass auto_ptr by reference.

1) Pass large structures: generally speaking, I agree.
2) Declare NULL of default value: depends on the function. Perhaps that
function may use NULL as an indication that the caller doesn't care about
the output buffer, so don't bother making one.
3) Still don't see why auto_ptr's should not be passed by reference.
From other side, compiler must prevents all errors, that can be
prevented. And compiler can issue warnings on wrong usage of any
methods with "once", "ctor", "dtor" and "after" modifiers of any
class. Consider

// ! not C++
void boo(const my_auto_ptr<Tobj>&);
char woo(const my_auto_ptr<Tobj>& ptr){ return (*ptr)? 1: 0; }
Tobj heap* my_auto_ptr<Tobj>::move()const dtor;

void foo(const my_auto_ptr<Tobj> ptr)
{
if:):is_ok){ptr.move();}

//warning - possible double "move()const dtor;"
//if "::is_ok" is not synced with "lib::all_ok()"
if(!lib::all_ok()){ptr.move();}

//warning - compiler lost control for
//"ptr having "once", "ctor", "dtor" or "after" modifiers"
//because body of boo is unknown
boo(ptr);

//ok - compiler know what are you doing
woo(ptr);
}

I think C++ must either define internal set of smart pointers (as
auto_ptr) with reliable complex behaviour, or define tools (working at
compile time) of language to allow users to make own reliable ordinary
classes as smart pointers.

Your method of avoiding compiler's problems with the help of declaring
objects as "non-const" (for non-const we are not expect any) is not
good, because it is just ignoring necessary static tests, as void
declaration is ignoring static check of type.

By reasons of my_auto_ptr<Tobj> logic, "non-const" is needed only if
you are going to assign new pointer value after my_auto_ptr<Tobj> has
created. Operation "move" is in dact destructor and destructor can be
applied to const object also, so "move" must be const.

Careful... remember that destructors are only called when the object is
ending its life.
You are rignt, technically you can not use object passed by reference
if the class of the object doesn't have any const methods.

But logically you sometimes also can not use object passed by const
reference, even if the class of the object have some const methods,
and class my_auto_ptr<Tobj> is example of this.

And thus you're confusing your potential users of your class. If I've
got a const object, I should be able to call const methods on it.
It is easy to see, that even so simple class as my_auto_ptr<Tobj> need
for special support from compiler (if we want remove extra runtime
testing). Declaring methods of the class as const or non-const is not
enough!

With the help of "once", "ctor", "dtor" and "after" modifiers of
methods of class we can remove runtime if-else operators for
conditions linked with "object state" and can remove extra data fields
keeping the flags of "object state" and can make testing of the
if-else of the conditions at compile time.

Again... how can the compiler enforce these across translation units?
With the help of the modifiers we can declare not only set of members
of interface, but in addition can declare sequence of calling the
methods of the interface.

Without them it is just as (...) or void - no check at compile time.



If you want to return ptr value, you can do like this

my_auto_ptr<Tobj> fn(const my_auto_ptr<Tobj>);
ptr=fn(ptr);

Whether I return it or not is irrelevant. The object was passed in
through a const-ref, and since your class breaks the const contract, I
cannot rely on the fact that ptr didn't change during the call to fn().
Thus making const useless.
Again - do not pass ptr to fn by reference. And you no need access to
fn()'s implementation. For class with explicit "once", "ctor", "dtor"
and "after" modifiers compiler can print warning that control of
object state lost due to reference.

You are habit, that passing by value no always possible, but there are
cases, when passing by reference is impossible too.

I can't think of any case where passing by reference (or pointer) is
impossible.
See upper - we need compile time checking, not runtime for so simple
class as auto_ptr.

That requires complete visibility of the object's lifespan. Not easily
possible (as far as I know) due to the separate compilation of the
translation units. Only the linker ends up seeing everything (and then
we get into the off-topic concept of dynamically linked translation units
and life gets even more complex).
But i am thinking at last of the letter, maybe C++ must allow to
declare class as disabled to pass the object of the class by
reference?

We can disable copy (pass by value) declaring copy ctor as protected.
Maybe expression "class_name&" or "const class_name&" can be treated
as operator and the operator can be declared as protected?

So far I haven't seen a useful purpose to disallowing pass by reference
(and by extention, pass by pointer).
 
G

Grizlyk

Andre Kostur wrote:

As outcome.

You are trying to declare auto_ptr as non-const in hope to warn user, that
auto_ptr has unexpected, hidden assignment to internal state. It is similar
to refusing from static type checking with the help of "void" - speaking "we
garantee nothing".

If assuming, that extraction from auto_ptr can occur only once (that is
garanteed by programmer because C++ can not do it), auto_ptr can be const
(and must be const if you want to disable explicit assignment -
"auto_ptr=" ).

The detailed consideration below:

Andre said:
As an example,
the intent of the auto_ptr class is to hold a pointer, and ensure that it
is deleted at the end of the life of the auto_ptr.

Exactly, but not complete.

There is other half of "the intent of the auto_ptr class" - ensure that it
(poniter) is moved (extracted from) to other auto_ptr or C-style pointer
_only once_, independent from lifetime of auto_ptr.

In other words, there is a piece of the auto_ptr life-time when the auto_ptr
has incorrect state and it is not error condition, it is predefined
(designed) condition.

We must demand last half of the auto_ptr state because the behaviour of the
auto_ptr is only useful and reliable among all other possible behaviours of
auto_ptr, because we do not want to introduce possible runtime errors
(especialy on such fundamental and low level as auto_ptr do) due to C++ does
not allow to control auto object, have destroyed befor object bacame
invisible and the memory of the object formally has returned into free
state.

Also we must demand last half of the auto_ptr state, because of behaviour of
the auto_ptr is not like behaviour of rvalue in expression, for example

auto_ptr a,b;
a=b;

is unrolled to

{
a=b::ptr, //ok
b=0; //error - unexpected assignment to rvalue from invisible
sourse
}

The same as "a=b", but correct expression is

//we do not expect here, that "b" will be unchanged
b.move(&a);

But we need auto_ptr to be able to be as rvalue and control memory
simultaneously.

It looks like "operator=" is wrong used here. We can introduce new C++
"operator<-" to reflect the fact of necessety to have "move-like" operations
(changing both rvalue and lvalue) for wrappers in C++

//we now can expect that state of "b" is changed
//we no need "non-const b" to warn user about
//the possible changes here
a<-b;

The "<-" excpression is busy by "<" and "-", so can be other variants: the
"=<" or "<--" looks ugly. Note that "<<" can not be used due to precedence
("=<" must have "=" priority) and due to rvalue of most C++ operators
expected as const. We need operator, rvalue of with is non-const by default.

New "operator<-" can help to programmer to understand what is going on, but
can not garantee at compile stage, that never will be double move (can not
garantee at compile stage, that any operation will not be applied to object
in incorrect state).

It is only way to do both goals safe - to have ability to tell to compiler
that from the point of "move" applied, auto_ptr de facto is destroyed, in
spite of the fact that formaly the auto_ptr is still exist and visible till
end of block.

The way to have ability to tell to compiler about is using "once", "ctor",
"dtor" and "after" keywords (mybe we need "befor" - to complete sequence).

The "state" of the
auto_ptr would be the contained pointer.

No, it is not. The "state" of the auto_ptr is not "contained pointer". It is
better to say that state of the auto_ptr is "stored pointer or incorrect",
state can not be divided. If assumig, that you will not use auto_ptr in
incorrect state, stored pointer can not be changed (excluding explicit
assignment new value to auto_ptr). Note, that non-const can not protect you
from usage of auto_ptr in incorrect state.

In the auto_ptr
example, it would be wrong to make the contained pointer mutable as it
contributes directly to the purpose of the class.

You are operating in wrong definition of "purpose of the auto_ptr".

Offhand I don't see how you could enforce your "once" keyword across
translation units at compile time. How would the system know that:

my_auto_ptr<Obj> ptr(new Obj);

fn(ptr);

ptr->get_pointer();

is legal or not? Remember that all the compiler knows is that fn() takes
a my_auto_ptr as a parameter, it doesn't know what its going to do with
it.

I have posted the example similar to this below:

We can see differences between boo(ptr) and woo(ptr). Compiler can support
even auto_ptr passed by reference but general solution for support testing
"across
translation units at compile time" - do not pass auto_ptr by reference - in
any case at least you will be warned by compiler that if you want the
reference you must take control of the usage of the auto_ptr.

1) Pass large structures: generally speaking, I agree.
2) Declare NULL of default value: depends on the function. Perhaps that
function may use NULL as an indication that the caller doesn't care about
the output buffer, so don't bother making one.

Agree, i have assumed bufer that will be used.
3) Still don't see why auto_ptr's should not be passed by reference.

Because you either can use the auto_ptr in external function (but can not
here), or can not use the auto_ptr in external function (but can here).
Passing the auto_ptr by reference allows you to use the auto_ptr in both
places, but that prohibited by auto_ptr declaration logic.

If you are not going to call "move" inside external function - pass ordinary
pointer, if you want to call "move" there - pass auto_ptr by value, because
you already will not be able to call "move" here (after extermal funtion
will return).

You can not distribute control of one part of memory into some separated
parts of unknown code without runtime tests and appearence of runtime
errors.

The useful limitation allows you to avoid usage hard shared_ptr instead of
easy auto_ptr.

Careful... remember that destructors are only called when the object is
ending its life.

The ordinary destructor yes, but like-destructor not duty. Object can be
into incorrect state befor ordinary destructor will be applied, but the
question is "can we tell to compiler about incorrect state of onject or can
not tell". The price of question is ability of compile time test instead of
runtime.

And thus you're confusing your potential users of your class. If I've
got a const object, I should be able to call const methods on it.

You can call const methods, it is independent from passing object by
reference. It can happend if C++ declares operators "to make reference for
function parameters" - the operators can be declared by user as protected
and you will be not able to compile code with function with references as
parameters due to "private in this context".

Again... how can the compiler enforce these across translation units?

See upper
That requires complete visibility of the object's lifespan.

No, compiler can do his best, and well-designed inplementations will be able
to use all advantages of compile time testing. The fact, that code can be
wrong structured must not disable useful compile time testing.

As for auto_ptr - the auto_ptr is local stuff - must be used inside one
scope and must not be passed to any other places, because its goal to do
memory management of new dynamic object passed to the scope (in fact to free
memory if function makes unexpected return).
Not easily
possible (as far as I know) due to the separate compilation of the
translation units.

If you want to give the control to any other places - make new auto_ptr.

So far I haven't seen a useful purpose to disallowing pass by reference
(and by extention, pass by pointer).

Yes, probably operator "reference" can control both reference and pointer,
not sure.

As outcome of your opinion - "you no need the property and the fact is
enough base, in order to refuse from include the property to language".

I think so:
- because you do not tell me how to solve the detecting troubles (to make
usage of auto_ptr safe), your main objection - "i have no any troubles";
- because you sometimes do not say no one convincing base of your decisions
(exclude your habits).

It is looks something like if we have fallen into dirt, then stood up and
said - there is no any dirt here :).
 
H

Howard Hinnant

"Grizlyk" <[email protected]> said:
It looks like "operator=" is wrong used here. We can introduce new C++
"operator<-" to reflect the fact of necessety to have "move-like" operations
(changing both rvalue and lvalue) for wrappers in C++

//we now can expect that state of "b" is changed
//we no need "non-const b" to warn user about
//the possible changes here
a<-b;

Fwiw, there is an entire "move semantics" package coming down the pike.
Here is a brief introduction which also includes references to more
detailed papers:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2027.html

One of the library parts of this proposal is an auto_ptr replacement:

unique_ptr

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1856.html#20.4.5
%20-%20Class%20template%20auto_ptr

Although I have not followed this thread closely, your discussion of
auto_ptr, rvalues, and move, led me to believe you might be interested
in this committee work.

-Howard
 
A

Andre Kostur

Grizlyk said:
Andre Kostur wrote:

As outcome.

You are trying to declare auto_ptr as non-const in hope to warn user,
that auto_ptr has unexpected, hidden assignment to internal state. It
is similar to refusing from static type checking with the help of
"void" - speaking "we garantee nothing".

No, non-const says that the function I'm calling may change the state of
the auto_ptr.
If assuming, that extraction from auto_ptr can occur only once (that
is garanteed by programmer because C++ can not do it), auto_ptr can be
const (and must be const if you want to disable explicit assignment -
"auto_ptr=" ).

You can "extract" from the auto_ptr as many times as you want. Just that
after the first one, the auto_ptr holds NULL, so that's all you're
getting.
The detailed consideration below:



Exactly, but not complete.

There is other half of "the intent of the auto_ptr class" - ensure
that it (poniter) is moved (extracted from) to other auto_ptr or
C-style pointer _only once_, independent from lifetime of auto_ptr.

Not entirely correct. If will enfore that only one auto_ptr may be
authoritative for a particular pointer.
In other words, there is a piece of the auto_ptr life-time when the
auto_ptr has incorrect state and it is not error condition, it is
predefined (designed) condition.

Nope. An auto_ptr holding a NULL pointer isn't incorrect state.
We must demand last half of the auto_ptr state because the behaviour
of the auto_ptr is only useful and reliable among all other possible
behaviours of auto_ptr, because we do not want to introduce possible
runtime errors (especialy on such fundamental and low level as
auto_ptr do) due to C++ does not allow to control auto object, have
destroyed befor object bacame invisible and the memory of the object
formally has returned into free state.

Proceeding from a false premise. auto_ptr doesn't have such a state.
auto_ptr contains a pointer upon which delete will be called when the
auto_ptr ends its life. The additional documentation for auto_ptr is
that copy & assignment are "destructive" operations in which the source
auto_ptr will be changed to NULL. There is no undefined state in
auto_ptr.
Also we must demand last half of the auto_ptr state, because of
behaviour of the auto_ptr is not like behaviour of rvalue in
expression, for example

auto_ptr a,b;
a=b;

is unrolled to

{
a=b::ptr, //ok
b=0; //error - unexpected assignment to rvalue from
invisible
sourse
}

Not an unexpected assignment. That's the definition of auto_ptr. It's
also why auto_ptr doesn't have a copy constructor which takes a const-
reference. (Or assignment operator).
The same as "a=b", but correct expression is

//we do not expect here, that "b" will be unchanged
b.move(&a);

But we need auto_ptr to be able to be as rvalue and control memory
simultaneously.

It looks like "operator=" is wrong used here. We can introduce new C++
"operator<-" to reflect the fact of necessety to have "move-like"
operations (changing both rvalue and lvalue) for wrappers in C++

//we now can expect that state of "b" is changed
//we no need "non-const b" to warn user about
//the possible changes here
a<-b;

The "<-" excpression is busy by "<" and "-", so can be other variants:
the "=<" or "<--" looks ugly. Note that "<<" can not be used due to
precedence ("=<" must have "=" priority) and due to rvalue of most C++
operators expected as const. We need operator, rvalue of with is
non-const by default.

New "operator<-" can help to programmer to understand what is going
on, but can not garantee at compile stage, that never will be double
move (can not garantee at compile stage, that any operation will not
be applied to object in incorrect state).

It is only way to do both goals safe - to have ability to tell to
compiler that from the point of "move" applied, auto_ptr de facto is
destroyed, in spite of the fact that formaly the auto_ptr is still
exist and visible till end of block.

The way to have ability to tell to compiler about is using "once",
"ctor", "dtor" and "after" keywords (mybe we need "befor" - to
complete sequence).



No, it is not. The "state" of the auto_ptr is not "contained pointer".
It is better to say that state of the auto_ptr is "stored pointer or
incorrect", state can not be divided. If assumig, that you will not
use auto_ptr in incorrect state, stored pointer can not be changed
(excluding explicit assignment new value to auto_ptr). Note, that
non-const can not protect you from usage of auto_ptr in incorrect
state.

auto_ptr has no incorrect state. It either holds a pointer (hopefully
valid and dynamically allocated, or you will be causing Undefined
Behaviour when auto_ptr goes out of scope), or it holds NULL. There's
nothing wrong in an auto_ptr holding NULL.
You are operating in wrong definition of "purpose of the auto_ptr".

Not as far as I can tell.
I have posted the example similar to this below:


We can see differences between boo(ptr) and woo(ptr). Compiler can
support even auto_ptr passed by reference but general solution for
support testing "across
translation units at compile time" - do not pass auto_ptr by reference
- in any case at least you will be warned by compiler that if you want
the reference you must take control of the usage of the auto_ptr.

But the warning will be useless. In order to cope with it, you still
need to provide runtime checks to see whether the boo() changed ptr or
not. And recall that you're trying to make all of this a compile-time
check.
Agree, i have assumed bufer that will be used.


Because you either can use the auto_ptr in external function (but can
not here), or can not use the auto_ptr in external function (but can
here). Passing the auto_ptr by reference allows you to use the
auto_ptr in both places, but that prohibited by auto_ptr declaration
logic.

Nope... there's nothing in auto_ptr's logic that prohibits an auto_ptr
from being used in both translation units. Perhaps the called function
may conditionally fill or destroy the contained memory of the auto_ptr.
(See example below)
If you are not going to call "move" inside external function - pass
ordinary pointer, if you want to call "move" there - pass auto_ptr by
value, because you already will not be able to call "move" here (after
extermal funtion will return).

Ah, but if you pass it by value, then the "source" variable will be
unchanged (or will have prematurely transferred ownership into the
function parameter), or you may conditionally call move:

void fn(auto_ptr<Obj> ptr)
{
auto_ptr<Obj> ptr2;

if (someCondition)
{
ptr2 = ptr.move();
}

// ptr2 goes out of scope, it may or may not have had a pointer
// transferred to it, so may or may not delete the memory.
// ptr goes out of scope, if ptr2 was transferred to it,
// it will delete the associated memory.
}

{
Obj * rawPtr = new Obj;
auto_ptr<Obj> pObj = rawPtr;

fn(pObj);

// pObj goes out of scope. How does it know whether to
// delete the memory or not? This function has no
// visibility of someCondition.
}
You can not distribute control of one part of memory into some
separated parts of unknown code without runtime tests and appearence
of runtime errors.

Yes you can (without the runtime errors part), if you pass an auto_ptr
into a function via non-const ref, it may come back as NULL. Check for
it. If you pass it into a function by value, you know it's now NULL. If
you pass it into a function by const-ref, you know it has not changed, no
check required.

And as I've mentioned above, your proposed mechanism is no better, and in
some cases worse (const-ref may change, so you need to check there, and
pass by value leaves the object in an indeterminate state).
The useful limitation allows you to avoid usage hard shared_ptr
instead of easy auto_ptr.



The ordinary destructor yes, but like-destructor not duty. Object can
be into incorrect state befor ordinary destructor will be applied, but
the question is "can we tell to compiler about incorrect state of
onject or can not tell". The price of question is ability of compile
time test instead of runtime.



You can call const methods, it is independent from passing object by
reference. It can happend if C++ declares operators "to make reference
for function parameters" - the operators can be declared by user as
protected and you will be not able to compile code with function with
references as parameters due to "private in this context".

That puts a blanket prohibition on ever calling the method.
See upper

No, compiler can do his best, and well-designed inplementations will
be able to use all advantages of compile time testing. The fact, that
code can be wrong structured must not disable useful compile time
testing.

Problem being that your compile time testing is then not providing the
safety that you're looking for. Even with such compile-time testing, you
still need to make runtime checks to see if your object is in a valid
state.
As for auto_ptr - the auto_ptr is local stuff - must be used inside
one scope and must not be passed to any other places, because its goal
to do memory management of new dynamic object passed to the scope (in
fact to free memory if function makes unexpected return).

See example above.
If you want to give the control to any other places - make new
auto_ptr.

See example above.
Yes, probably operator "reference" can control both reference and
pointer, not sure.

As outcome of your opinion - "you no need the property and the fact is
enough base, in order to refuse from include the property to
language".

I think so:
- because you do not tell me how to solve the detecting troubles (to
make usage of auto_ptr safe), your main objection - "i have no any
troubles"; - because you sometimes do not say no one convincing base
of your decisions (exclude your habits).

If you pass an auto_ptr into a function via non-const ref, it may come
back as NULL. Check for it. If you pass it into a function by value,
you know it's now NULL. If you pass it into a function by const-ref, you
know it has not changed, no check required.

Granted this assumes:

1) Both programmers are writing well-formed C++ code that does not invoke
undefined behaviour.
2) The function implementor has correctly stated his intention for the
function parameter by declaring it appropriately (const-ref to auto_ptr
if it's not going to change the auto_ptr, but needs the object to be an
auto_ptr for whatever reason. Reference to auto_ptr if it may change the
pointer, Value of auto_ptr if it is intended to be a sink for the memory.
Reference to obj (or obj by value, if appropriate) if it only cares about
the contained object. Pointer to obj if it only cares about the
contained object, and may be NULL.)
 
G

Grizlyk

Andre said:
No, non-const says that the function I'm calling may change the state of
the auto_ptr.

You can not define "state" of auto_ptr in free manner. Your definition of
auto_ptr as "structure containing pointer" is place of runtime errors and
can not be accepted as best definition. The reasons to make "state" of
auto_ptr as "structure containing pointer" is not based on any conclusions
excluding that "auto_ptr has pointer".

You can not consider "state" of auto_ptr separated from auto_ptr goal. The
goal of auto_ptr - extract pointer _only once_ that can be done for const
auto_ptr as for non-const auto_ptr. Are you agree with me, that "extract
pointer only once" can be done for const auto_ptr? If yes, auto_ptr must be
const.

Note, that "move" is not the same as "get_ptr". The "get_ptr" can be applied
to auto_ptr many times befor first "move" and no one time after "move".
You can "extract" from the auto_ptr as many times as you want. Just that
after the first one, the auto_ptr holds NULL, so that's all you're
getting.

We are speaking about useful extraction, it is evidently, no reason to
extract predefined NULL from auto_ptr, it is just error in your code, much
easy to write NULL directly without "auto_ptr.move()".

I have no one example of real code, where i wanted to extract from auto_ptr
at least twice but have enough examples, when i found possible runtime
errors due to double extraction.
Not entirely correct. If will enfore that only one auto_ptr may be
authoritative for a particular pointer.

Wait. I do not understand - if you think, that pointer can be extracted from
auto_ptr more than once and you are thinking that following sequence is
correct:

{
my_auto_ptr<char> ptr(new char[100]);

char *q=ptr.move(); strcpy(q,"huraaa");
char *w=ptr.move(); strcpy(w,"huraaa");
}

but following is incorrect due to "const":

{
const my_auto_ptr<char> ptr(new char[100]);

char *q=ptr.move(); strcpy(q,"huraaa");
char *w=ptr.move(); strcpy(w,"huraaa");
}

I have no words to continue.
 
A

Andre Kostur

Grizlyk said:
Andre said:
No, non-const says that the function I'm calling may change the state
of the auto_ptr.

You can not define "state" of auto_ptr in free manner. Your definition
of auto_ptr as "structure containing pointer" is place of runtime
errors and can not be accepted as best definition. The reasons to make
"state" of auto_ptr as "structure containing pointer" is not based on
any conclusions excluding that "auto_ptr has pointer".

You can not consider "state" of auto_ptr separated from auto_ptr goal.
The goal of auto_ptr - extract pointer _only once_ that can be done
for const auto_ptr as for non-const auto_ptr. Are you agree with me,
that "extract pointer only once" can be done for const auto_ptr? If
yes, auto_ptr must be const.

Note, that "move" is not the same as "get_ptr". The "get_ptr" can be
applied to auto_ptr many times befor first "move" and no one time
after "move".
You can "extract" from the auto_ptr as many times as you want. Just
that after the first one, the auto_ptr holds NULL, so that's all
you're getting.

We are speaking about useful extraction, it is evidently, no reason to
extract predefined NULL from auto_ptr, it is just error in your code,
much easy to write NULL directly without "auto_ptr.move()".

I have no one example of real code, where i wanted to extract from
auto_ptr at least twice but have enough examples, when i found
possible runtime errors due to double extraction.
Not entirely correct. If will enfore that only one auto_ptr may be
authoritative for a particular pointer.

Wait. I do not understand - if you think, that pointer can be
extracted from auto_ptr more than once and you are thinking that
following sequence is correct:

{
my_auto_ptr<char> ptr(new char[100]);

Let's assume that you meant "new Obj". Giving a pointer to array into
auto_ptr (or my_auto_ptr) will have Undefined Behaviour as the class will
call delete and not delete[] on the pointer.
char *q=ptr.move(); strcpy(q,"huraaa");

Sure. (OK... assuming that Obj will cast itself into a char *, and it
will make sense as a C-string). q has the newed pointer, and ptr
contains NULL.
char *w=ptr.move(); strcpy(w,"huraaa");

Undefined Behaviour. You're pulling out a NULL pointer, and then passing
it to strcpy. Which is presumably the behaviour you're worried about and
want the compiler to stop it in some manner.
}

but following is incorrect due to "const":

{
const my_auto_ptr<char> ptr(new char[100]);

char *q=ptr.move(); strcpy(q,"huraaa");

Yep. The compiler will (or should) stop you here since you've declared
ptr as const, and thus isn't allowed to let go of the pointer.
char *w=ptr.move(); strcpy(w,"huraaa");

Same deal.
 
G

Grizlyk

You forget to answer here: Are you agree or not?


Have you any examples constradicts to my examples here?


Do you think that pointer can be extracted from auto_ptr more than once?

and if you are thinking that following sequence is correct:

{
my_auto_ptr<char> ptr(new char[100]);

Let's assume that you meant "new Obj".

I agree, that real my_auto_ptr must be parametrised by "memory" object like
this

#define heap[]
#define heap

template<class Tobj>
class array
{
public:
Tobj heap[]* ptr;

void replace(Tobj heap* p){delete[] ptr; ptr=p;}
void destroy(){delete[] ptr; ptr=p;}
};

template<class Tobj>
class object
{
public:
Tobj heap* ptr;

void replace(Tobj heap* p){delete ptr; ptr=p;}
void destroy(){delete ptr; ptr=p;}
};

that is why i had to write "my_auto_ptr<char,array>" instead of
Giving a pointer to array into
auto_ptr (or my_auto_ptr) will have Undefined Behaviour as the class will
call delete and not delete[] on the pointer.
char *q=ptr.move(); strcpy(q,"huraaa");

Sure. (OK... assuming that Obj will cast itself into a char *, and it
will make sense as a C-string). q has the newed pointer, and ptr
contains NULL.
char *w=ptr.move(); strcpy(w,"huraaa");

Undefined Behaviour. You're pulling out a NULL pointer, and then passing
it to strcpy. Which is presumably the behaviour you're worried about and
want the compiler to stop it in some manner.

It is not UB, it is always _runtime logical error_ (even program will not be
terminated by hardware memory manager) because we were going to write into
"new char[100]", not to "NULL". It is segmentation fault on most systems
with hardware memory management, because access to NULL is obvious error.

Let it is Undefined Behaviour, tell me please, second ptr.move() calling is
correct code? Is it error visible at compile time?

}

but following is incorrect due to "const":

{
const my_auto_ptr<char> ptr(new char[100]);

char *q=ptr.move(); strcpy(q,"huraaa");

Yep. The compiler will (or should) stop you here since you've declared
ptr as const, and thus isn't allowed to let go of the pointer.

I understand nothing - "move" is declared as "const" and that is why can be
called for "const ptr".

Tobj heap* my_auto_ptr<Tobj,Tmemory>::move()const;

But tell me, please, could the const modifier of "my_auto_ptr<Tobj,Tmemory>"
disable extraction from ptr with the help of "move()const"?

Same deal.

Exactly, the same, but what is original object to "same" :)?
 
A

Andre Kostur

Grizlyk said:
You forget to answer here: Are you agree or not?

No, you can never "extract" the pointer from a const auto_ptr. You may
read it, but not extract it.
Have you any examples constradicts to my examples here?

The auto_ptr may have been filled by a different translation unit, so you
may not know in advance as to whether the auto_ptr holds a pointer to
memory somewhere, or a NULL.
Do you think that pointer can be extracted from auto_ptr more than
once?

Nope, (at least not the same pointer value) but it is far more useful to
say that you may never extract the pointer from the auto_ptr.
and if you are thinking that following sequence is correct:

{
my_auto_ptr<char> ptr(new char[100]);

Let's assume that you meant "new Obj".

I agree, that real my_auto_ptr must be parametrised by "memory" object
like this

#define heap[]
#define heap

template<class Tobj>
class array
{
public:
Tobj heap[]* ptr;

void replace(Tobj heap* p){delete[] ptr; ptr=p;}
void destroy(){delete[] ptr; ptr=p;}
};

template<class Tobj>
class object
{
public:
Tobj heap* ptr;

void replace(Tobj heap* p){delete ptr; ptr=p;}
void destroy(){delete ptr; ptr=p;}
};

that is why i had to write "my_auto_ptr<char,array>" instead of
Giving a pointer to array into
auto_ptr (or my_auto_ptr) will have Undefined Behaviour as the class
will call delete and not delete[] on the pointer.
char *q=ptr.move(); strcpy(q,"huraaa");

Sure. (OK... assuming that Obj will cast itself into a char *, and
it will make sense as a C-string). q has the newed pointer, and ptr
contains NULL.
char *w=ptr.move(); strcpy(w,"huraaa");

Undefined Behaviour. You're pulling out a NULL pointer, and then
passing it to strcpy. Which is presumably the behaviour you're
worried about and want the compiler to stop it in some manner.

It is not UB, it is always _runtime logical error_ (even program will
not be terminated by hardware memory manager) because we were going to
write into "new char[100]", not to "NULL". It is segmentation fault on
most systems with hardware memory management, because access to NULL
is obvious error.

Since it's a runtime logical error, it can't fail at compile time since
the compiler doesn't know what's going to happen at runtime.
Let it is Undefined Behaviour, tell me please, second ptr.move()
calling is correct code? Is it error visible at compile time?

Nope (at least not to the compiler). Within this specific context, it
will invoke Undefined Behaviour since we, as the programmers, can see
what .move() is doing behind the scenes which will cause UB.
}

but following is incorrect due to "const":

{
const my_auto_ptr<char> ptr(new char[100]);

char *q=ptr.move(); strcpy(q,"huraaa");

Yep. The compiler will (or should) stop you here since you've
declared ptr as const, and thus isn't allowed to let go of the
pointer.

I understand nothing - "move" is declared as "const" and that is why
can be called for "const ptr".

And thus the crux of the argument. You're breaking the concept of
"const" by allowing a const method to change the state of the object.
Tobj heap* my_auto_ptr<Tobj,Tmemory>::move()const;

But tell me, please, could the const modifier of
"my_auto_ptr<Tobj,Tmemory>" disable extraction from ptr with the help
of "move()const"?

Thing is, move changes the state of the object, and thus should not be
const.
Exactly, the same, but what is original object to "same" :)?

Shouldn't be able to call move on the const object.
 
A

Andre Kostur

Grizlyk said:
void replace(Tobj heap* p){delete[] ptr; ptr=p;}

Oops!

void replace(Tobj heap* p)
{
if( p != ptr )
{
if(ptr)delete[] ptr;

You don't need the "if (ptr)" part. Calling delete[] on a NULL pointer is
well-defined, and is a no-op.
 
M

mlimber

Not sure what you want to say with that.

If you search the archives, you'll see that Roland picks this same
fight every so often. He believes that smart pointers violate the
principle of RAII, whereas most everyone else (Stroustrup, Sutter, et
al.) believe smart pointers enable it.

Cheers! --M
 
K

Kai-Uwe Bux

mlimber said:
If you search the archives, you'll see that Roland picks this same
fight every so often. He believes that smart pointers violate the
principle of RAII, whereas most everyone else (Stroustrup, Sutter, et
al.) believe smart pointers enable it.

Well, he has a point: technically, smart pointers implement an ownership
model in a half-hearted way (by allowing initialization from a raw pointer
that co-owns the pointee). Those ownership policies probably would be
better implemented by a smart reference (that also would ensure the
existence of an owned object). However, since C++ does not allow
overloading the dot-operator, smart pointers have filled this void and
became the de-facto standard for ready-to-use implementations of ownership
models. From a purely technical point of view, a somewhat sub-optimal
solution.

Moreover, Mr Pibinger seems to be fighting the notion that smart pointers
are a panacea to avoid memory management issues (like the C++ substitute
for garbage collection). He is not totally off-target in this regard,
either. I tend to use tr1::shared_ptr<> if and only if I have a shared
resource (like a communications channel) that needs to be destroyed when
and only when its last client disappears. I tend not to use shared_ptr<> to
take care of, say, reclaiming memory in implementations of container
classes.


Nonetheless, this is by and large a pointless debate as (a) there are no
smart references that could replace smart pointers and (b) shared_ptr<> is
now standardized with initialization from a raw pointer setting a
precedence. Also, one should not always ask which devious mischief a
certain interface allows for, but whether there is an easy to learn set of
idioms that enable safe and sound use of a class. Since everbody can be
taught to initialize a smart pointer like

shared_ptr<T> ptr ( new T (some_arg) );

I do not see that much of problem with smart pointers in this regard.


Best

Kai-Uwe Bux
 
G

Grizlyk

Andre said:
void replace(Tobj heap* p){delete[] ptr; ptr=p;}

Oops!

void replace(Tobj heap* p)
{
if( p != ptr )
{
if(ptr)delete[] ptr;

You don't need the "if (ptr)" part. Calling delete[] on a NULL pointer is
well-defined, and is a no-op.

I can pass NULL to delete but I some times ago i decided to do with explicit
condition, because with "if (ptr)" execution is faster, because most often
condition is "false", because pointer in most cases have moved out befor
auto_ptr is going to destroy, also there are many intermediate temporary
auto_ptr objects tracking the way of last auto_ptr - all of intermediate
objects always are destroying with "false" condition. In other words we can
eliminate unnecessary "delete" function call because "cmp + jmp" is faster
than "push +call +ret +pop"

Look to ASM

-1-
; if(ptr)delete ptr;
;
@1:
cmp dword ptr [_ptr],0
//often
je short @2

//rare
push dword ptr [_ptr]
call @$bdele$qpv
pop ecx

-2-
; delete ptr;
;
//often extra call of "delete"
push dword ptr [_p]
call @$bdele$qpv
pop ecx
 
G

Grizlyk

Andre said:
No, you can never "extract" the pointer from a const auto_ptr. You may
read it, but not extract it.

I have tired to convince you that independend from "const" modifier the goal
of const/non-const auto_ptr to garantee, that pointer will be extracted only
once.

It is evidently to anybody, that work this NULL via auto_ptr (NULL is stored
in auto_ptr after first extraction) is error and previous example
{
/*const*/ my_auto_ptr<char> ptr(new char[100]);

char *q=ptr.move(); strcpy(q,"huraaa");
char *q=ptr.move(); strcpy(q,"huraaa");
}

is illustrating the case.

If modification of auto_ptr will occur only once, it does not metter const
the object or not const. It is evidently also.

There are no one back side effects, because ptr member of auto_ptr is
declared as 'mutable".

Due to disagreeing with it we are just running always on the one place. I
see, your strategy of defence is denial of the obvious facts, it is
implemented as refusing to publish any reasons of your decisions, forgeting
to answer, answering to other questions and so on.

You can of course win in many discussions "by persistence", but any program
based on such answers will do not work, as car will do not drive without
fuel, in spite of all words that fuel is not need.

I am thinking: what are the reasons to do like this? The problem of const of
auto_ptr is neutral for both of us. Maybe habits to non-const?

In some cases it does not metter const or non-const, but in general const is
always better and always must be applied if it can.

The std::auto_ptr have only one advantage befor const auto_ptr, and the
advantage is not "non-const", but that std::auto_ptr belong to "namespace
std".

The following example can be compied NOW (copy & paste it to file) and
Ntest::Tauto_ptr can be used as const. It is best argument that i am right.
Maybe it is happy future of C++ :)

- cut here -
#include <typeinfo>
#include <exception>

//sorry
#include <stdio.h>

//C++ still not support
#define heap
#define array //heap[]
#define _auto

#define ctor
#define dtor
#define after(x)
#define befor(x)
#define once
#define probe

#define memtype

//debug
#define MY_DBG

// **********************************************************
namespace Ntest
{
using std::bad_cast;


// *****************
template<class Tobj>
class Tarray
{
public:
//C++ still not support
//typedef array memtype;

Tobj array* ptr;

public:
void detach(){ ptr=0; }
void destroy(){ if(ptr)delete[] ptr; ptr=0; }

void replace(Tobj array* p)
{
if( p != ptr )
{
if(ptr)delete[] ptr;
ptr=p;
}
}

Tobj array* move()
{
Tobj array *const tmp=ptr;
ptr=0;
return tmp;
}

public:
Tarray(Tobj array* p=0):ptr(p){}
};

// *****************
template<class Tobj>
class Tobject
{
public:
//C++ still not support
//typedef heap memtype;

Tobj heap* ptr;

public:
void detach(){ ptr=0; }
void destroy(){ if(ptr)delete ptr; ptr=0; }

void replace(Tobj heap* p)
{
if( p != ptr )
{
if(ptr)delete ptr;
ptr=p;
}
}

Tobj heap* move()
{
Tobj heap* const tmp=ptr;
ptr=0;
return tmp;
}

public:
Tobject(Tobj heap* p=0):ptr(p){}
};

// *****************
template<class Tobj>
class Tadr
{
public:
//C++ still not support
//typedef _auto memtype;

Tobj _auto* ptr;

public:
void detach(){ ptr=0; }
void destroy(){ ptr=0; }
void replace(Tobj _auto* p){ ptr=p; }

Tobj _auto* move()
{
Tobj _auto* const tmp=ptr;
ptr=0;
return tmp;
}

public:
Tadr(Tobj _auto* p=0):ptr(p){}
};

// **********************************************
template<class Tobj, class Tmemory=Tobject<Tobj> >
class Tauto_ptr
{
mutable Tmemory memory;
//typedef typename Tmemory::memtype memtype;

Tauto_ptr& set_ptr(Tobj memtype* const new_adr)
{
memory.replace(new_adr);
return *this;
}

void created()
{
#ifdef MY_DBG
printf("%p: created\n",this);
#endif
}

public:
char is_ptr_assigned()const probe { return memory.ptr? 1:0; }

Tobj _auto* get_ptr()const after(set_ptr||created) { return
static_cast<Tobj _auto*>(memory.ptr); }
Tauto_ptr& replace(Tobj heap* const new_adr)ctor { return
set_ptr(new_adr); }
Tobj memtype* move()const dtor { return memory.move(); }
void detach()const dtor { memory.detach(); }

public:
Tobj& get_obj()const { if(!memory.ptr)throw bad_cast(); return
*get_ptr(); }

Tobj& operator* ()const { return get_obj(); }
Tobj _auto* operator-> ()const { return &get_obj(); }

Tobj& operator() ()const { return get_obj(); }
Tobj _auto* operator() (const int)const { return get_ptr(); }

Tauto_ptr& operator= (Tobj heap* const new_adr)ctor { return
replace(new_adr); }

public:
Tauto_ptr(Tobj memtype* p=0):memory(p){created();}
Tauto_ptr(const Tauto_ptr& obj):memory(obj.move()){created();}
Tauto_ptr& operator= (const Tauto_ptr& obj)ctor
{
if( &obj != this ){ replace(obj.move()); }
return *this;
}
~Tauto_ptr(){memory.destroy();}
};

// *****************
//namespace Ntest
}

// *****************
// *****************

using namespace Ntest;

typedef Tauto_ptr<char,Tarray<char> > Ta_ptr;

Ta_ptr storage;

void test(const Ta_ptr msg)
{
#ifdef MY_DBG
printf( "%p->%p: %s\n",&msg, msg(0), msg(0) );
#else
printf( "%s\n", msg(0) );
#endif
storage=msg;
}

int main()
{
try{

Ta_ptr tmp;

tmp=new char[100];
#ifdef MY_DBG
printf("%p->%p\n",&tmp, tmp(0) );
#endif
snprintf(tmp(0),100,"%s","I think 'const Tauto_ptr' is good.");

test(tmp);
#ifdef MY_DBG
printf("%p->%p: storage: %s\n",&storage, storage(0), storage(0) );
#else
printf("storage: %s\n",storage(0) );
#endif

tmp=new char[100];
#ifdef MY_DBG
printf("%p->%p\n",&tmp, tmp(0) );
#endif
snprintf(tmp(0),100,"%s","We all agree.");

test(tmp);
#ifdef MY_DBG
printf("%p->%p: storage: %s\n",&storage, storage(0), storage(0) );
#else
printf("storage: %s\n",storage(0) );
#endif

printf("\nOK\ntesting 'bad_cast'\n");
//testing bad_cast
tmp();
}
catch(std::bad_cast&){fprintf(stderr,"error: %s\n","bad_cast");}
catch(...){fprintf(stderr,"error: %s\n","unknown");}

}

- cut here -
 

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

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,218
Latest member
JolieDenha

Latest Threads

Top