Why doesn't auto_ptr construction work using = syntax
Asked Answered
U

4

9

I ran into a compiler error that didn't make much sense to me:

#include <memory>
using namespace std;

auto_ptr<Table> table = db->query("select * from t");

error: conversion from 'Table*' to non-scalar type 'std::auto_ptr< Table>' requested

However, the following line does work:

auto_ptr<Table> table(db->query("select * from t"));

What is it about this definiton of the constructor that prevents it from working as I expect? I thought that initialized declarations used the constructors.

Here's my auto_ptr's constructor (from the SGI STL):

explicit
auto_ptr(element_type* __p = 0) throw() : _M_ptr(__p) { }
Upper answered 16/4, 2009 at 18:32 Comment(0)
G
17

It's the "explicit" keyword.

template <typename T>
struct foo
{
  explicit foo(T const *)
  {
  }
};


template <typename T>
struct bar
{
  bar(T const *)
  {
  }
};


int main(int argc, char **argv)
{
  int a;
  foo<int> f = &a; // doesn't work
  bar<int> b = &a; // works
}

The "explicit" keyword prevents the constructor from being used for implicit type conversions. Consider the following two function prototypes:

void baz(foo<int> const &);
void quux(bar<int> const &);

With those definitions, try calling both functions with an int pointer:

baz(&a);  // fails
quux(&a); // succeeds

In the case of quux, your int pointer was implicitly converted to a bar.

EDIT: To expand on what other people commented, consider the following (rather silly) code:

void bar(std::auto_ptr<int>);


int main(int argc, char **argv)
{
  bar(new int()); // probably what you want.

  int a;
  bar(&a); // ouch. auto_ptr would try to delete a at the end of the
           // parameter's scope

  int * b = new int();
  bar(b);
  *b = 42; // more subtle version of the above.
}
Gyp answered 16/4, 2009 at 18:38 Comment(7)
Ugh. Just when I convince myself that I have actually found a use for auto_ptr, it's design smacks me in the face. Back to go old delete.Upper
Don't drop auto_ptr just for that. Just use the explicit constructor: auto_ptr<Table> table(db->query("select * from t")); Or move the shared_ptr.Dispassion
There's a very good reason for the constructor to be explicit. It protects you from accidental mistakes. It's easy enough to put the auto_ptr<T>(...) around.Brokenhearted
You shouldn't be using equals in that case anyways... it's as though you are relying on the compiler to optimize for you. Simply recognize that the ctor of auto_ptr requires a T* and construct appropriately. Why in the world cause a construction and copy when you can have 1 construction?Alecto
not sure that, "relying on the compiler to optimize for you" is a bad thing in general - but in this instance it's a case of idiomsHegelian
There is no compiler optimization here. Both syntaxes always translate to exactly the same - invocation of the initializing c'tor.Tympanitis
@Tom, it's not an optimization it's a part of the language. Class v = ... is supposed to be semantically equivalent to Class v(...). auto_ptr as usual breaks the rules.Upper
B
8

You need to use

auto_ptr<Table> table = auto_ptr<Table>(db->query("select * from t"));

auto_ptr does not define an assignment operator for it's template type. The only allowed assignment is from another auto_ptr (and it's constructor from the pointer is explicit). This is done to protect accidental misuse of auto_ptr, as auto_ptr assumes ownership of the memory.

My guess is that you need the assignment form to use multiple queries after another like:

// initialize using constructor
auto_ptr<Table> table(db->query("select * from t1"));
...
// new query using assignment
table = auto_ptr<Table>(db->query("select * from t2"));
...
// another query using assignment
table = auto_ptr<Table>(db->query("select * from t3"));
Brokenhearted answered 16/4, 2009 at 18:35 Comment(7)
Why not auto_ptr<Table> table(db->query("select * from t"));?Wanids
Well he figured that uou himself (see his question). And the assign form is necessary whenever you do more than one query after another :-)Brokenhearted
I wish there was a way to edit comments. uou - damn fast typing ;-)Brokenhearted
you can delete comments (then re-enter, sans-typo)Hegelian
lothar, I wish for comment edits too. And for Markdown when we're at it. :)Wanids
uh yeah ;-) I would love markdown in comments (at least a subset like italics, bold, ...)Brokenhearted
Why not use the reset() function. table.reset( db->query("select * from t1") );Elsy
H
5

The constructor is declared as explicit, which means that it won't be used for implicit type casting. Implicit conversion to auto_ptr could easily lead to undesirable situations since the auto_ptr is taking ownership of the pointer.

For example, if auto_ptr would allow implicit conversion from a pointer and you accidentally passed a pointer to a method taking an auto_ptr the pointer would be silently converted to an auto_ptr and subsequently deleted when the function ends, even if that wasn't the intention. But by marking the constructor as explicit conversion can no longer happen silently and by calling the constructor you clearly express the intention of passing ownership to the auto_ptr, thus avoiding any potential confusion.

void fun(std::auto_ptr<Foo> foo) // Assume implicit conversion is allowed.
{
    // do stuff with foo
}

Foo *foo = new Foo();

f(foo); // Normally this isn't allowed.

foo->bar(); // Oops
Hartmunn answered 16/4, 2009 at 19:1 Comment(0)
L
2

Adding to what lothar said: Because the auto_ptr constructor is declared with the explicit keyword, you need to use an explict cast to create an auto_ptr from a raw pointer. (Before the introduction of explicit, implicit casting was the bane of many a new -- and experienced) C++ developer.)

Loth answered 16/4, 2009 at 18:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.