Использование std::shared_ptr с clang++ и libstdC++
Я пытаюсь использовать std::shared_ptr в clang ++ (clang версии 3.1 (trunk 143100)) с использованием libstdC++(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.)? У кого-нибудь есть идеи, как это исправить?
2 ответа
Неявно объявленный конструктор копирования для shared_ptr удален, потому что shared_ptr имеет конструктор перемещения или оператор присваивания перемещения (или оба) для C++11 12.8p7:
Если определение класса не объявляет явно конструктор копирования, он объявляется неявно. Если определение класса объявляет конструктор перемещения или оператор присваивания перемещения, неявно объявленный конструктор копирования определяется как удаленный; в противном случае он определяется как дефолтный (8.4).
GCC 4.6.x не реализует это правило, которое вошло в рабочий документ C++ 11 очень поздно, так как N3203 = 10-0193. На момент написания файла shared_ptr в libstdC++ 4.6.x был верным, но после этого C++ 11 изменился. У Boost возникла точно такая же проблема с его shared_ptr, и это одна из распространенных несовместимостей между GCC и Clang.
Добавление по умолчанию конструктора копирования и оператора назначения копирования к shared_ptr решит проблему.
Заголовки стандартной библиотеки для 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)
Но не имеет надлежащего конструктора копирования (шаблонный конструктор не может использоваться компилятором в качестве конструктора копирования). Я нахожу странным, однако, что такая ошибка возникнет с производственным компилятором...
РЕДАКТИРОВАТЬ Я пытался выяснить, почему именно g++ 4.6 компилирует вышеуказанный код с его собственной стандартной библиотекой. Кажется, что он собирает template
d конструктор как жизнеспособная перегрузка для создания копии, что заставило меня оглянуться назад к стандарту, чтобы проверить, является ли это ошибкой - у меня всегда было впечатление, что шаблон не может использоваться в качестве конструктора копирования - или нет.
В стандарте C++03 есть сноска 106)
Поскольку конструктор шаблона никогда не является конструктором копирования, наличие такого шаблона не подавляет неявное объявление конструктора копирования. Конструкторы шаблона участвуют в разрешении перегрузки с другими конструкторами, включая конструкторы копирования, и конструктор шаблона может использоваться для копирования объекта, если он обеспечивает лучшее соответствие, чем другие конструкторы.
Этой сноски нет в C++11. Сноски не являются нормативными, поэтому должна быть другая цитата, поддерживающая эту заметку. Несколько параграфов ниже вы можете найти:
12.8 / 3 Объявление конструктора для класса X некорректно, если его первый параметр имеет тип (необязательно cv-квалифицированный) X и либо нет других параметров, либо все остальные параметры имеют аргументы по умолчанию. Шаблон функции-члена никогда не создается для выполнения копирования объекта класса в объект его типа.
Первая часть абзаца означает, что конструктор не может принимать тот же тип, что и аргумент (конструкторы копирования принимают ссылки, и разрешение такого конструктора приведет к неоднозначности, а также к тому факту, что для этого потребуется копирование в аргумент, для которого наилучшая перегрузка - предполагая, что это запретило конструктор копирования - была бы та же самая функция, которая в свою очередь потребовала бы... бесконечный цикл).
Во второй части предложения говорится, что шаблонный конструктор не может использоваться для создания копий типа, что, по-видимому, является нормативным разделом, поддерживающим сноску 106). Теперь это было тщательно переписано в C++ 11, чтобы заявить:
12.8 / 6 Объявление конструктора для класса X является некорректным, если его первый параметр имеет тип (необязательно cv-квалифицированный) X и либо нет других параметров, либо все остальные параметры имеют аргументы по умолчанию. Шаблон функции-члена никогда не создается для создания такой сигнатуры конструктора.
Теперь это означает, что ограничение, которое шаблон нельзя использовать для копирования, было удалено и заменено менее строгим ограничением: шаблон не будет мгновенно создан для создания конструктора формы S( S )
то есть тот, который вызовет неоднозначность с конструктором копирования.
С этим меньшим ограничением кажется, что шаблонный конструктор выше может фактически использоваться как конструктор копирования, поскольку генерируемая им подпись совместима. Это поддерживает поведение компилятора g++ 4.6 при обработке bits/shared_ptr.h
заголовок, и будет означать, что clang++
не может использовать шаблон в качестве допустимого конструктора.
Теперь следующий вопрос: совместима ли стандартная реализация библиотеки, поставляемая с g++ 4.6, или нет. Я не могу сказать от макушки головы. С одной стороны, отсутствует подпись конструктора, о которой я упоминал выше, поэтому вы можете утверждать, что она не соответствует требованиям. Но с другой стороны, совместимый компилятор должен подобрать шаблонный конструктор для достижения той же функциональности, и реализация будет вести себя так, как если бы этот конструктор присутствовал.