Jean-Denis Muys
Jean-Denis Muys

Reputation: 6842

C++ constructor prevents successful compile

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

Answers (2)

Luchian Grigore
Luchian Grigore

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

ForEveR
ForEveR

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

Related Questions