Конструктор С++ предотвращает успешную компиляцию

Я столкнулся со странной проблемой на С++, и я был бы признателен за объяснение. Этот фрагмент не компилируется:

size_t bufLength = 18;
char* buffer = new char[bufLength];
auto_array_ptr<char> pBuffer1 = buffer;  // fails
auto_array_ptr<char> pBuffer2(buffer);

3-я строка выше не работает с No viable constructor copying variable of type 'auto_array_ptr<char>'. Обратите внимание, что следующая строка компилируется просто отлично.

Q1) Это само по себе странно для меня. Я думал, что инициализация с присваиванием трансформируется в инициализацию с инициализатором, когда это необходимо. Почему первый может потерпеть неудачу, если второй успешен?

Q2) Но настоящая загадка для меня заключается в том, что ошибочная строка завершается успешно, когда я удаляю конструктор из класса auto_array_ptr: конструктор auto_array_ptr(auto_array_ptr). Мне действительно трудно понять, что здесь происходит.

Я могу представить себе сценарий того, что компилятор может попытаться сделать здесь:

1- искать пустоту operator=(char *p). Не найден. Посмотрим, сможем ли мы продвигать аргумент (buffer). 2- ах ах, есть operator=(auto_array_ptr&). Так что я выиграю, если смогу повысить buffer до auto_array_ptr. Давайте найдем конструктор для этого. 3- ах ах есть конструктор auto_array_ptr(auto_array_ptr&). Итак, давайте создадим временную переменную из buffer, используя этот конструктор (temp). 4- теперь попробуйте использовать метод operator=(auto_array_ptr&),. Но облом: его аргумент не const, и я не могу его использовать. Сообщить об ошибке.

Но этот сценарий неубедителен. Во-первых, компилятор может заметить, что проблема const — это шаг 2. Кроме того, он может использовать auto_array_ptr(char *) напрямую, а не пытаться продвигать buffer. Затем, если я добавлю в класс operator=(char *p), ошибка не исчезнет. Наконец, это не объясняет, почему удаление auto_array_ptr(auto_array_ptr&) помогает.

Конечно, вам нужен источник auto_array_ptr. вот:

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;
};

Компилятор представляет собой последнюю версию Clang, работающую в Xcode 4.4 под Mac OS X Lion. Я считаю, что он основан на LLVM 3.1. Чуть более свежая версия в Xcode 4.5 ведет себя точно так же.

Спасибо.


person Jean-Denis Muys    schedule 03.09.2012    source источник
comment
Кроме того, связанный - stackoverflow.com/questions/11222076/   -  person Luchian Grigore    schedule 03.09.2012


Ответы (2)


auto_array_ptr<char> pBuffer1 = buffer;  // fails

создайте temporary auto_array_ptr<char> из buffer и скопируйте его в pBuffer1.

auto_array_ptr(auto_array_ptr<T>& a)

ваш copy c-tor получает reference. Вы не можете связать temporary с reference, поэтому компиляция завершается ошибкой.

Q2) Но настоящая загадка для меня заключается в том, что ошибочная строка завершается успешно, когда я удаляю конструктор из класса auto_array_ptr: конструктор auto_array_ptr(auto_array_ptr). Мне действительно трудно понять, что здесь происходит.

Компилятор создаст copy c-tor с signature

auto_array_ptr(const auto_array_ptr&)
person ForEveR    schedule 03.09.2012
comment
Другими словами, ошибка ОП заключается в том, что он не отделяет конструкцию клонирования (т. Е. Копирование конструкции того же типа) от неявного приведения к этому типу. Если у OP есть доступ к объявлению auto_array_ptr, он должен добавить конструктор копирования с семантикой перемещения. - person Stefan Majewsky; 03.09.2012
comment
У меня есть доступ к auto_array_ptr с ограничениями (мне нужно обосновать любые его изменения). Бывает так, что сбойная строка прекрасно компилируется под VC++. Так что я должен быть в состоянии объяснить необходимость изменений. Вот что будет сигнатурой «конструктора копирования с семантикой перемещения» (если не реализацией)? Также обратите внимание, что я, к сожалению, не могу использовать С++ 11. - person Jean-Denis Muys; 04.09.2012
comment
@Jean-DenisMuys Компилятор не может создать копию c-tor, если есть явное объявление копии c-tor (определяется пользователем). VC умеет компилировать, т.к. если копия будет пропущена, MSVC не проверит, подходит ли копия c-tor. Это ошибка в MSVC. Для получения дополнительной информации об этой ошибке см. оператора OK msvs, но не работает в g">stackoverflow.com/questions/12001054/ - person ForEveR; 04.09.2012

Это инициализация копирования:

auto_array_ptr<char> pBuffer1 = buffer;  // fails

Это создает временный auto_array_ptr<char> из buffer, а затем использует конструктор копирования для pBuffer1. (при условии оптимизации) Это не удается, потому что конструктор копирования объявлен как:

auto_array_ptr(auto_array_ptr<T>& a)

и временное не может быть привязано к ссылке, отличной от const. Измените его на

auto_array_ptr(const auto_array_ptr<T>& a)

Это прямая инициализация:

auto_array_ptr<char> pBuffer2(buffer);

использует только конструктор преобразования.

person Luchian Grigore    schedule 03.09.2012
comment
OP на самом деле находится в привязке, потому что изменение конструктора копирования на const& будет означать, что вы вызываете release (который освобождает право собственности и, таким образом, изменяет состояние) для объекта const. - person Matthieu M.; 04.09.2012
comment
Я понимаю. Означает ли это, что нет законного способа использовать инициализацию копирования для такого класса интеллектуальных указателей? - person Jean-Denis Muys; 04.09.2012