Reputation: 6842
I am facing s strange issue in C++ and I'd appreciate an explanation. This snippet fails to compile:
size_t bufLength = 18;
char* buffer = new char[bufLength];
auto_array_ptr<char> pBuffer1 = buffer; // fails
auto_array_ptr<char> pBuffer2(buffer);
The 3rd line above fails with No viable constructor copying variable of type 'auto_array_ptr<char>'
. Note that the next line compiles just fine.
Q1) This in itself is strange to me. I thought initialisation with an assignment was transformed into initialisation with an initialiser when necessary. Why could the first fail if the second succeeds?
Q2) But the real puzzle for me is that the failing line succeeds when I remove a constructor from the auto_array_ptr
class: the auto_array_ptr(auto_array_ptr)
constructor. I really have a hard time understanding what's happening here.
I can sort of imagine a scenario here of what the compiler might be trying to do here:
1- look for a void operator=(char *p)
. Not found. Let's see if we can promote the argument (buffer
).
2- ah ah, there is an operator=(auto_array_ptr&)
. So I win if I can promote buffer
to an auto_array_ptr
. Let's look for a constructor to do that.
3- ah ah there is a auto_array_ptr(auto_array_ptr&)
constructor. So let's construct a temporary variable from buffer
using that constructor (temp
).
4- now try to use the operator=(auto_array_ptr&)
, method. But bummer: its argument is not const
, and I can't actually use it. Report an error.
But this scenario is not convincing. For one, the compiler could notice the const
problem is step 2. Also, it could use the auto_array_ptr(char *)
directly rather than trying to promote buffer
. Then, if I add a operator=(char *p)
to the class, the error doesn't go away. Finally, that doesn't explain why removing the auto_array_ptr(auto_array_ptr&)
helps.
Of course you need the source of auto_array_ptr
. here it is:
template<class T>
class auto_array_ptr
{
public:
auto_array_ptr(T *p = 0) : ptr(p) {}
auto_array_ptr(auto_array_ptr<T>& a) : ptr(a.release()) {} // remove this line to compile
~auto_array_ptr() {if(ptr != 0) {delete[] ptr; ptr = 0;}}
void operator=(auto_array_ptr<T>& a) {if(&a != this) reset(a.release());}
// void operator=(T *p) { if(p != ptr) reset(p);} // adding this doesn't help
T& operator[](int i) const {return ptr[i];}
T& operator[](unsigned int i) const {return ptr[i];}
operator T*() const {return ptr;}
T* get() const {return ptr;}
T* release() {T* tmp = ptr; ptr = 0; return tmp;}
void reset(T *p = 0) {if(ptr != 0) {delete[] ptr;}; ptr = p;}
private:
T *ptr;
};
The compiler is a recent version of Clang running in Xcode 4.4 under Mac OS X Lion. I believe it's based on LLVM 3.1. A slightly more recent version in Xcode 4.5 behaves exactly the same.
Thanks.
Upvotes: 4
Views: 385
Reputation: 258618
This is copy initialization:
auto_array_ptr<char> pBuffer1 = buffer; // fails
This creates a temporary auto_array_ptr<char>
from buffer
and then uses the copy constructor for pBuffer1
. (subject to optimizations) This fails because the copy constructor is declared as:
auto_array_ptr(auto_array_ptr<T>& a)
and a temporary cannot bind to a non-const
reference. Change it to
auto_array_ptr(const auto_array_ptr<T>& a)
This is direct initialization:
auto_array_ptr<char> pBuffer2(buffer);
only uses the conversion constructor.
Upvotes: 3
Reputation: 55887
auto_array_ptr<char> pBuffer1 = buffer; // fails
create temporary auto_array_ptr<char>
from buffer
and copy it to pBuffer1
.
auto_array_ptr(auto_array_ptr<T>& a)
your copy c-tor
receive reference
. You cannot bind temporary
to reference
, so, compilation fails.
Q2) But the real puzzle for me is that the failing line succeeds when I remove a constructor from the auto_array_ptr class: the auto_array_ptr(auto_array_ptr) constructor. I really have a hard time understanding what's happening here.
Compiler will create copy c-tor
with signature
auto_array_ptr(const auto_array_ptr&)
Upvotes: 4