Можно ли определить конструкторы копирования контейнеров как удаленные для не копируемых типов значений?

Если у нас есть контейнер с не копируемым типом значения, такой контейнерный класс все еще определяет конструктор копирования, просто он может не вызываться.

using T = std::vector<std::unique_ptr<int>>;
std::cout << std::is_copy_constructible_v<T>; // prints out "1" (libstdc++)

Это может вызвать "скрытые" проблемы, такие как обсуждаемая здесь: Нужно ли Visual Studio 2017 явное объявление конструктора перемещения? ,

Мой вопрос заключается в том, могут ли реализации стандартной библиотеки определить конструктор копирования как условно удаленный, а именно удаленный в случае не копируемых типов значений. Это имело бы смысл для меня (по крайней мере, пока не появятся концепции C++). Будет ли такая реализация соответствовать стандарту?

Ответ 1

Это математически невозможно с тех пор, как vector получил поддержку неполного типа:

struct E {
    std::vector<E> e;
};

E копируемый, если std::vector<E> копируемый, а std::vector<E> копируемый, если E копируемый. Черепахи все время вниз.

Даже до этого, поскольку construct распределителя может искажать аргументы конструктора так, как считает нужным, и у контейнера нет способа определить, является ли что-то "конструируемым распределителем", условное удаление конструктора копирования потребовало бы некоторых серьезных проектных работ. Неполная поддержка типа просто положить гвоздь в гроб.

Ответ 2

Для краткого ответа: нет. Если мы посмотрим на текущую спецификацию (начиная с С++ 17) std :: vector, у нас будет следующая подпись и описание:

vector(const vector& other);

Скопируйте конструктор. Создает контейнер с копией содержимого другого. Если alloc не предоставлен, allocator получается как будто путем вызова std :: allocator_traits :: select_on_container_copy_construction (other.get_allocator()).

Конструктор копирования имеет обычную каноническую подпись, и в описании не указано никаких условий SFINAE, поэтому соответствующая реализация не должна налагать более строгие требования, такие как условное удаление. Тем не менее, ошибка создания экземпляра произойдет, если будет предпринята попытка явного или неявного обращения к vector<unique_ptr<T>> copy, поскольку описание подразумевает поэлементное копирование. Таким образом, vector<unique_ptr<T>> не удовлетворяет требованию CopyConstructible, что очень похоже на наличие конструктора удаленной копии.

Насколько я знаю, нет синтаксической поддержки условного удаления, но условия SFINAE и возможные ограничения могут обеспечить выборочное разрешение перегрузки. Я все еще настоятельно рекомендую не использовать их в специальных операциях. Специальные операции должны быть определены с использованием их обычной канонической подписи.

Ответ 3

Как говорит TC, это может быть даже неосуществимо, но если бы это было так, я полагаю, что раздел [member.functions] p2 в разделе Соответствие реализации не позволяет этого:

Для невиртуальной функции-члена, описанной в стандартной библиотеке C++, реализация может объявить другой набор сигнатур функций-членов при условии, что любой вызов функции-члена, который выберет перегрузку из набора объявлений, описанных в этом документе ведет себя так, как будто эта перегрузка была выбрана. [Примечание: Например, реализация может добавить параметры со значениями по умолчанию или заменить функцию-член аргументами по умолчанию двумя или более функциями-членами с эквивалентным поведением, или добавить дополнительные подписи для имени функции-члена. - конец примечания]