Использование std:: shared_ptr с clang++ и libstdС++
Я пытаюсь использовать std:: shared_ptr в clang++ (clang version 3.1 (trunk 143100)), используя libstdС++ (4.6.1). У меня есть небольшая демонстрационная программа:
#include <memory>
int main()
{
std::shared_ptr<int> some(new int);
std::shared_ptr<int> other(some);
return 0;
}
который можно построить с помощью:
clang++ -std=c++0x -o main main.cpp
и выдает следующий вывод ошибки:
main.cpp:6:23: error: call to deleted constructor of 'std::shared_ptr<int>'
std::shared_ptr<int> other(some);
^ ~~~~
/usr/include/c++/4.6/bits/shared_ptr.h:93:11: note: function has been explicitly marked
deleted here
class shared_ptr : public __shared_ptr<_Tp>
По какой-то причине ему нужен конструктор, который удаляется, поскольку предоставляется конструктор перемещения (что является правильным поведением).
Но зачем он компилируется с помощью (g++ (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1.)? Кто-нибудь знает, как это исправить?
Ответы
Ответ 1
Неявно объявленный конструктор копирования для shared_ptr удаляется, поскольку shared_ptr имеет конструктор перемещения или оператор присваивания перемещения (или оба), для С++ 11 12.8p7:
Если определение класса явно не объявляет конструктор копирования, он объявляется неявно. Если определение класса объявляет конструктор перемещения или перемещает оператор присваивания, неявно объявленный конструктор копирования определяется как удаленный; в противном случае он определяется как дефолт (8.4).
GCC 4.6.x не реализует это правило, которое появилось в рабочем документе С++ 11 очень поздно, поскольку N3203 = 10-0193. Файл shared_ptr в libstdС++ 4.6.x был правильным в момент его написания, но после этого С++ 11 изменился. Boost имел точно такую же проблему с ним shared_ptr, и это одна из общих несовместимостей между GCC и Clang.
Добавление созданного по умолчанию конструктора копии и оператора назначения копирования в shared_ptr устранит проблему.
Ответ 2
Стандартные заголовки библиотек для gcc 4.6 кажутся неправильными в этом случае, поскольку для стандарта требуется следующий конструктор (§20.7.2.2.1/16):
shared_ptr(const shared_ptr& r) noexcept;
Какой экземпляр-конструктор, который, кажется, отсутствует в реализации gcc. Реализация, которую я имею под рукой (g++ - 4.6.0), предлагает (в bits/shared_ptr.h
):
template<typename _Tp1>
shared_ptr(const shared_ptr<_Tp1>& __r)
Но не имеет надлежащего конструктора-копии (шаблонный конструктор не может использоваться компилятором в качестве конструктора копирования). Мне кажется странным, что такая ошибка возникла бы с помощью компилятора...
EDIT Я пытался выяснить, почему именно g++ 4.6 компилирует вышеуказанный код с собственной стандартной библиотекой. Кажется, что он собирает конструктор template
d как жизнеспособную перегрузку для построения копии, что заставило меня взглянуть на стандарт, чтобы проверить, является ли это ошибкой. Я всегда находился под впечатлением, что шаблон не мог был использован как конструктор копирования - или нет.
В стандарте С++ 03 есть сноска 106)
Поскольку конструктор шаблонов никогда не является конструктором копирования, наличие такого шаблона не подавляет неявное объявление- конструктора копии. Конструкторы шаблонов участвуют в разрешении перегрузки с другими конструкторами, включая copy construc- tors и конструктор шаблонов могут использоваться для копирования объекта, если он обеспечивает лучшее совпадение, чем другие конструкторы.
Эта сноска отсутствует в С++ 11. Сноски не являются нормативными, поэтому должна быть другая цитата, поддерживающая эту заметку. Ниже вы найдете несколько параграфов:
12.8/3 Объявление конструктора для класса X плохо сформировано, если его первый параметр имеет тип (необязательно cv-квалифицированный) X и либо нет других параметров, либо все остальные параметры имеют аргументы по умолчанию. Шаблон функции-члена никогда не создается для выполнения копии объекта класса для объекта своего типа.
Первая часть параграфа означает, что конструктор не может принимать тот же тип, что и аргумент (конструкторы копирования берут ссылки, и разрешая такой конструктор вызвать неоднозначность, это и тот факт, что он потребует копирования в аргумент, для которого лучшая перегрузка - полагая, что это блокирует конструктор копирования - будет той же функцией, которая по очереди потребует... бесконечный цикл).
Вторая часть предложения гласит, что шаблонный конструктор нельзя использовать для создания копий типа, который, как представляется, является нормативным разделом, поддерживающим сноску 106). Теперь, что было тщательно переформулировано в С++ 11, чтобы указать:
12.8/6 Объявление конструктора для класса X плохо сформировано, если его первый параметр имеет тип (необязательно cv-квалифицированный) X, и либо нет других параметров, либо все остальные параметры имеют аргументы по умолчанию. Шаблон функции-члена никогда не создается для создания такой сигнатуры-конструктора.
Теперь это означает, что ограничение шаблона не может быть использовано для копирования, было удалено и заменено менее строгим ограничением: шаблон не будет создан для создания конструктора формы S( S )
, то есть один что приведет к двусмысленности с конструктором копирования.
При этом меньшем ограничении кажется, что шаблонный конструктор, приведенный выше, фактически может быть использован в качестве конструктора-копии, поскольку подпись, которую он создает, совместима. Это поддерживает поведение компилятора g++ 4.6 при обработке заголовка bits/shared_ptr.h
и подразумевает, что clang++
не использует шаблон в качестве допустимого конструктора.
Теперь следующий вопрос: совместима ли стандартная реализация библиотеки с g++ 4.6 или нет. Я не могу сказать с головы. С одной стороны, отсутствует подпись конструктора, о которой я упоминал выше, поэтому вы можете утверждать, что она не соответствует требованиям. Но, с другой стороны, совместимый компилятор должен забрать шаблонный конструктор для достижения той же функциональности, и реализация будет вести себя как-если бы этот конструктор присутствовал.