0

I want to write a C++ class with a constructor which takes an auto_ptr as its argument so that I could initialize class instances from auto_ptrs to another instance:

#include <memory>

class A
{
public:
  A() {}
  A(std::auto_ptr<A> other) {}
};

std::auto_ptr<A> create()
{
  return std::auto_ptr<A>(new A());
}

void foo()
{
  A x = create();
  // A y ( create() );    // works
}

Compiling this code with g++ -c test.cpp on gcc 4.6 yields the following error messages:

test.cpp: In function ‘void foo()’:
test.cpp:17:16: error: no matching function for call to ‘std::auto_ptr<A>::auto_ptr(std::auto_ptr<A>)’
test.cpp:17:16: note: candidates are:
/usr/include/c++/4.6/backward/auto_ptr.h:260:7: note: std::auto_ptr<_Tp>::auto_ptr(std::auto_ptr_ref<_Tp>) [with _Tp = A]
/usr/include/c++/4.6/backward/auto_ptr.h:260:7: note:   no known conversion for argument 1 from ‘std::auto_ptr<A>’ to ‘std::auto_ptr_ref<A>’
/usr/include/c++/4.6/backward/auto_ptr.h:125:9: note: std::auto_ptr<_Tp>::auto_ptr(std::auto_ptr<_Tp1>&) [with _Tp1 = A, _Tp = A]
/usr/include/c++/4.6/backward/auto_ptr.h:125:9: note:   no known conversion for argument 1 from ‘std::auto_ptr<A>’ to ‘std::auto_ptr<A>&’
/usr/include/c++/4.6/backward/auto_ptr.h:112:7: note: std::auto_ptr<_Tp>::auto_ptr(std::auto_ptr<_Tp>&) [with _Tp = A, std::auto_ptr<_Tp> = std::auto_ptr<A>]
/usr/include/c++/4.6/backward/auto_ptr.h:112:7: note:   no known conversion for argument 1 from ‘std::auto_ptr<A>’ to ‘std::auto_ptr<A>&’
test.cpp:7:3: error:   initializing argument 1 of ‘A::A(std::auto_ptr<A>)’

However, if I use the syntax A y (create()); to create my object, it works.

I want to know why this happens and if there is anything I can do to work around it.

EDIT: I will also point out that if I change the constructor signature to

  A(const std::auto_ptr<A>& other) {}

then everything works beautifully, however this doesn't take ownership of the auto_ptr and thus doesn't have the semantics that I want.

EDIT 2: If I do the same thing with an assignment operator, i.e.,

A& operator=( std::auto_ptr<A> other) {}

then I can do

A x;
x = create();

Why?

cfh
  • 4,576
  • 1
  • 24
  • 34

2 Answers2

7

You are only allowed one implicit, user-defined conversion. Constructing an auto_ptr from another one already involves the implicit conversion through an auxiliary auto_ptr_ref class, and so you cannot implicitly construct your own class from an auto_ptr.

By using direct initialization, one of the conversions is explicit, and only one implicit user-defined conversion remains, which is fine.

To "work around" the lack of implicit conversion, you can either modify your constructor to take the auto_ptr by (non-const) reference, or migrate everything to unique_ptrs.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Thanks, good answer. I didn't know about the "only one implicit conversion" rule. The dark corners of C++. Is there an online source for this? – cfh Oct 28 '13 at 20:05
  • @eriatarka84: It's a fairly sensible rule if you think about it. If there's more than one conversion needed, there's no way in general to decide which chain of conversions is "the right one". Any good introductory book should describe implicit conversions. – Kerrek SB Oct 28 '13 at 20:13
  • That's funny, I've been using C++ for more than 15 years and yet I don't know all the stuff that's in an introductory book. I wonder if that says more about me or the language. – cfh Oct 28 '13 at 20:17
0

Use:

A(std::auto_ptr<A>& other)
              //  ^ Note the reference!
{
    // Assign interned auto_ptr member here, which you definitely should have
}
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
  • 2
    That won't work, you can't bind temporaries (as the one returned from `create()`) to non-const references. – cfh Oct 28 '13 at 19:24
  • I've been just talking about the compiler errors mentioned in particular. There are other bugs in the OP's code yes! – πάντα ῥεῖ Oct 28 '13 at 19:26
  • Or flaws. To declare the `create()` method returning a `std::auto_ptr`, by value, to have no member to intern created auto_ptr' s, to have such method at public scope all, etc. Ah' sorry, you are the OP actually, didn't notice that. – πάντα ῥεῖ Oct 28 '13 at 19:36
  • 1
    `create()` is a function, not a method. And how else would you return an `auto_ptr` but by value? – cfh Oct 28 '13 at 19:39
  • That simply doesn't work for `std::auto_ptr`, because it's not possible to create it from a const copy itself! It want's to change ownership of the interned pointer. You might trying to do trickery using `std::autp_ptr::release()` but I don't recommend to do so. – πάντα ῥεῖ Oct 28 '13 at 19:43
  • @KerrekSB Explains this much better than me technically. But leads to the same result. – πάντα ῥεῖ Oct 28 '13 at 19:46
  • 1
    I think you didn't read the code carefully. The `auto_ptr` in the `create()` function is created from a new `A` instance, which is a standard usage pattern for `auto_ptr`. Such functions are sometimes called sources. – cfh Oct 28 '13 at 19:46
  • I did read your code, it does work for other cases, but not for construction! – πάντα ῥεῖ Oct 28 '13 at 19:48