explicit auto_ptr<T>::auto_ptr(T*) ?

S

Sousuke

The constructor for auto_ptr<T> which takes a T* is explicit, which is
requiring me to create a temporary when passing a T* to a function
that takes an auto_ptr<T> (by value). Something like this:

void f1(auto_ptr<int> p)
{
}

void f2()
{
int* p = new int;
// I have to do either:
f1(static_cast<auto_ptr<int> >(p));
// or:
//f1(auto_ptr<int>(p));
}

In another case, I have an auto_ptr<T>, where T is a subclass of the T
of the auto_ptr parameter of the function I'm calling:

class A { public: virtual ~A() {} };
class B : public A {};

void f1(auto_ptr<A> p)
{
}

void f2()
{
auto_ptr<B> p(new B);
// I still have to do either:
f1(static_cast<auto_ptr<A> >(p));
// or:
//f1(auto_ptr<A>(p));
}

I would expect that by calling:
f1(p);
the template constructor of auto_ptr<T> which takes an
auto_ptr<Other>& would be called, where Other is the template
parameter and where T* is compatible with Other*. However, the
compiler says:
error C2664: 'f1' : cannot convert parameter 1 from
'std::auto_ptr<_Ty>' to 'std::auto_ptr<_Ty>'
with
[
_Ty=B
]
and
[
_Ty=A
]
No user-defined-conversion operator available that can perform
this conversion, or the operator cannot be called

Does auto_ptr have some magic to avoid the creation of a temporary in
either of these cases?
 
M

mzdude

The constructor for auto_ptr<T> which takes a T* is explicit, which is
requiring me to create a temporary when passing a T* to a function
that takes an auto_ptr<T> (by value). Something like this:

void f1(auto_ptr<int> p)
{

}
IMHO passing auto_ptr as a function parameter is usually
wrong. You are giving the function lifetime management of
the resource. You probably want
void f2()
{
    int* p = new int;
    // I have to do either:
    f1(static_cast<auto_ptr<int> >(p));
    // or:
    //f1(auto_ptr<int>(p));

}
Now you would write
auto_ptr<int> p(new int);
f1(p);
no extra copies and no additional casting required.
In another case, I have an auto_ptr<T>, where T is a subclass of the T
of the auto_ptr parameter of the function I'm calling:

class A { public: virtual ~A() {} };
class B : public A {};

void f1(auto_ptr<A> p)
{

}
If you have control of the code perhaps
template<typename T>
void f2()
{
    auto_ptr<B> p(new B);
    // I still have to do either:
    f1(static_cast<auto_ptr<A> >(p));
    // or:
    //f1(auto_ptr<A>(p));

}
now becomes
I would expect that by calling:
f1(p);
the template constructor of auto_ptr<T> which takes an
auto_ptr<Other>& would be called, where Other is the template
parameter and where T* is compatible with Other*. However, the
compiler says:
error C2664: 'f1' : cannot convert parameter 1 from
'std::auto_ptr<_Ty>' to 'std::auto_ptr<_Ty>'
        with
        [
            _Ty=B
        ]
        and
        [
            _Ty=A
        ]
        No user-defined-conversion operator available that can perform
this conversion, or the operator cannot be called

Does auto_ptr have some magic to avoid the creation of a temporary in
either of these cases?
 
S

Sousuke

IMHO passing auto_ptr as a function parameter is usually
wrong. You are giving the function lifetime management of
the resource. You probably want
void f1(auto_ptr<A> &p) {}

But the point of using auto_ptr instead of a plain pointer in the
first place is that it explicitly states that the function takes
ownership of the passed pointer (not to mention that it deletes the
pointer at the end of the function and makes the code exception-safe).
Passing an auto_ptr said:
Now you would write
    auto_ptr<int> p(new int);
    f1(p);
no extra copies and no additional casting required.







If you have control of the code perhaps
template<typename T>
void f1(auto_ptr<T> &p)
{}





now becomes
   auto_ptr<B> p(new B);
   f1(p);

That sort of solves the problem if the function takes an auto_ptr<T>&
(a reference), but I don't think that's a good idea.

Anyway, I think I'll have to settle with the temporary :)
I would expect that by calling:
f1(p);
the template constructor of auto_ptr<T> which takes an
auto_ptr<Other>& would be called, where Other is the template
parameter and where T* is compatible with Other*. However, the
compiler says:
error C2664: 'f1' : cannot convert parameter 1 from
'std::auto_ptr<_Ty>' to 'std::auto_ptr<_Ty>'
        with
        [
            _Ty=B
        ]
        and
        [
            _Ty=A
        ]
        No user-defined-conversion operator available that can perform
this conversion, or the operator cannot be called
Does auto_ptr have some magic to avoid the creation of a temporary in
either of these cases?
 
M

mzdude

But the point of using auto_ptr instead of a plain pointer in the
first place is that it explicitly states that the function takes
ownership of the passed pointer (not to mention that it deletes the
pointer at the end of the function and makes the code exception-safe).
Passing an auto_ptr<T>& doesn't do any of those things.

auto_ptr does document who owns the resource. I think whenever
possible
whoever creates the resource should release the resource. It is
expected behaviour.

Ex 1:
auto_ptr<int> p(new int);
f1(p);

is just as exception safe as

Ex 2:
int *p = new int;
f1(auto_ptr<int>(p));

The difference is under maintenance someone may
try to use p at a later date. Which of course leads
to undefined behaviour since the memory was released.

I see no advantage to Ex 2 and there are definitely
obfucsation issues. Although I have no data to back it
up, I think Ex 1 is more idiomatic C++.

if you are really concerned about scoping then
Ex 1a:
{
auto_ptr<int> p(new int);
f1(p);
}

now the resource is given up on the completion of f1().

void f2()
{
auto_ptr<B> p(new B);
// I still have to do either:
f1(static_cast<auto_ptr<A> >(p));
// or:
//f1(auto_ptr<A>(p));
}


A quick quesition. Just after f1() returns back to f2() is
p still pointing to a valid object? If you have to think about
this, then reconsider the interface.
 
M

mzdude

  That still doesn't guarantee that the function won't transfer the
ownership, though... This might even change if the function is modified
later, breaking the calling code.

nothing would ever guarantee that it won't break under maintenance.
But it
is less likely if common coding conventions are followed.
  At least when passing by value the behavior is consistent (ie. the
ownership is always transferred).

The behaviour is consistent, but my IMHO too many contortions are
required by the calling code for no additional benefits. The code
is not more secure, and the object life time is exactly the same.
 
S

Sousuke

auto_ptr does document who owns the resource. I think whenever
possible
whoever creates the resource should release the resource. It is
expected behaviour.

In my actual code this isn't really possible (or practical, anyway).
The 'f1' function actually looks like:

void DecoratorArray::Add(auto_ptr<PrefixDecorator> decorator)
{
m_prefixDecorators.push_back(decorator.get());
decorator.release();
}

where m_prefixDecorators is a private member of DecoratorArray
declared like this:

vector<PrefixDecorator*> m_prefixDecorators;

and where PrefixDecorator is an abstract class.

Whoever adds objects to an instance of this "array" class forgets
about them (until they are needed again, at which time another method
in DecoratorArray is called to access them). Finally, they are deleted
by DecoratorArray's destructor:

DecoratorArray::~DecoratorArray()
{
for (vector<PrefixDecorator*>::iterator iter =
m_prefixDecorators.begin(); iter != m_prefixDecorators.end(); ++iter)
delete *iter;
// some more code...
}
Ex 1:
  auto_ptr<int> p(new int);
  f1(p);

is just as exception safe as

Ex 2:
   int *p = new int;
   f1(auto_ptr<int>(p));

The difference is under maintenance someone may
try to use p at a later date. Which of course leads
to undefined behaviour since the memory was released.

In some places (those where I need to manipulate the pointer prior to
adding it to the array) I'm doing what Ex 1 does. In other places I
just do:

f1(auto_ptr<int>(new int));

but I wish I could avoid the temporary by just doing:

f1(new int);
I see no advantage to Ex 2 and there are definitely
obfucsation issues. Although I have no data to back it
up, I think Ex 1 is more idiomatic C++.

if you are really concerned about scoping then
Ex 1a:
  {
    auto_ptr<int> p(new int);
    f1(p);
  }

now the resource is given up on the completion of f1().

But the intent is that f1 should take on the ownership of the passed
pointer.
A quick quesition. Just after f1() returns back to f2() is
p still pointing to a valid object?

Nope; f1 takes ownership.
 
J

James Kanze

The constructor for auto_ptr<T> which takes a T* is explicit,
which is requiring me to create a temporary when passing a T*
to a function that takes an auto_ptr<T> (by value).

Whether a conversion is implicit or explicit doesn't change
anything with regards to temporaries. Depending on the context,
making a conversion implicit reduces the number of characters
you have to type, and increases the risk of error and the
probability of ambiguity. Generally, the balance weighs against
implicit conversions (but I'm not so sure here).
Something like this:
void f1(auto_ptr<int> p)
{
}
void f2()
{
int* p = new int;
// I have to do either:
f1(static_cast<auto_ptr<int> >(p));
// or:
//f1(auto_ptr<int>(p));
}
And?

In another case, I have an auto_ptr<T>, where T is a subclass
of the T of the auto_ptr parameter of the function I'm
calling:
class A { public: virtual ~A() {} };
class B : public A {};
void f1(auto_ptr<A> p)
{
}
void f2()
{
auto_ptr<B> p(new B);
// I still have to do either:
f1(static_cast<auto_ptr<A> >(p));
// or:
//f1(auto_ptr<A>(p));
}
I would expect that by calling:
f1(p);
the template constructor of auto_ptr<T> which takes an
auto_ptr<Other>& would be called, where Other is the template
parameter and where T* is compatible with Other*.

This is what I would expect too. If I read the standard
correctly, it's also what the standard requires; the converting
constructor:
However, the compiler says:
error C2664: 'f1' : cannot convert parameter 1 from
'std::auto_ptr<_Ty>' to 'std::auto_ptr<_Ty>'
with
[
_Ty=B
]
and
[
_Ty=A
]
No user-defined-conversion operator available that can perform
this conversion, or the operator cannot be called

It's hard to say. It may be a bug in the compiler, but auto_ptr
has undergone a number of changes in time, and it's possible
that the library simply isn't up to date.
Does auto_ptr have some magic to avoid the creation of a
temporary in either of these cases?

Again: having an implicit conversion doesn't eliminate the
temporary. And making it explicit doesn't create an additional
temporary.
 
J

James Kanze

mzdude wrote:

[...]
If the function is taking an auto_ptr as parameter, there
ought to be a reason for that. Usually if it takes an auto_ptr
as parameter, it means it *wants* ownership of the object, and
auto_ptr achieves exactly that. It makes little sense to give
it an object using an auto_ptr otherwise.

The usual reason for using auto_ptr as a parameter is not for
memory management per se; it's that the calling code no longer
has the right to access the pointed to object. I use auto_ptr
extensively in my threading interfaces---once you've passed the
object to another thread, you'd better not access it, because if
you do, a race condition will result.
 
J

James Kanze

auto_ptr does document who owns the resource. I think whenever
possible whoever creates the resource should release the
resource.

Not really. Most of the time, when whoever creates the resource
should release it, there are even simpler solutions, like
scoped_ptr. You use auto_ptr explicitly when this is not the
case. (Or, of course, when you have auto_ptr, but not
scoped_ptr:).) Most of the time, if whoever creates the
resource should free it, you shouldn't be using dynamic
allocation to begin with (but there are a lot of exceptions).
 
B

Bart van Ingen Schenau

The constructor for auto_ptr<T> which takes a T* is explicit, which is
requiring me to create a temporary when passing a T* to a function
that takes an auto_ptr<T> (by value). Something like this:

The temporary has to be created anyway. Either explicitly by you or
implicitly by the compiler.
That the auto_ptr<T>::auto_ptr(T*) constructor is explicit is a good
thing, because it prevents you from writing stuf like this without any
warning that it will blow-up at runtime:

void f1(auto_ptr<int> p);
//...
void f2()
{
int i;

f1(&i); // Is this correct? No, but only a warning because the
constructor is explicit.
}

Bart v Ingen Schenau
 

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,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top