tags:

views:

597

answers:

4

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) { }
+7  A: 

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"));
lothar
Why not `auto_ptr<Table> table(db->query("select * from t"));`?
avakar
Well he figured that uou himself (see his question). And the assign form is necessary whenever you do more than one query after another :-)
lothar
I wish there was a way to edit comments. uou - damn fast typing ;-)
lothar
you can delete comments (then re-enter, sans-typo)
Phil Nash
lothar, I wish for comment edits too. And for Markdown when we're at it. :)
avakar
uh yeah ;-) I would love markdown in comments (at least a subset like italics, bold, ...)
lothar
Why not use the reset() function. table.reset( db->query("select * from t1") );
TimW
+11  A: 

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.
}
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.
Frank Krueger
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.
Eclipse
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.
lothar
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?
Tom
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 idioms
Phil Nash
There is no compiler optimization here. Both syntaxes always translate to exactly the same - invocation of the initializing c'tor.
shoosh
@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.
Frank Krueger
+1  A: 

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.)

Dan Breslau
+4  A: 

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
Samuel Otter