Std::vector, построение по умолчанию, С++ 11 и нарушение изменений
Я побежал сегодня против довольно тонкого вопроса, на который я хотел бы высказать свое мнение.
Рассмотрим следующий садово-парковый класс с общим телом и идиомой:
struct S
{
S() : p_impl(new impl) {}
private:
struct impl;
boost::shared_ptr<impl> p_impl;
};
Веселье появляется, когда вы пытаетесь поместить их в векторы следующим образом:
std::vector<S> v(42);
Теперь, по крайней мере, с MSVC 8, все элементы в v
имеют один и тот же элемент impl
. Фактически, причиной этого является конструктор vector
:
template <typename T, typename A = ...>
class vector
{
vector(size_t n, const T& x = T(), const A& a = A());
...
};
Под сценариями создается только один объект S
, элементы n
vector
копируются из него.
Теперь, с С++ 11, есть ссылки rvalue. Поэтому он не может работать так. Если a vector
построено как
std::vector<S> v(42);
то, скорее всего, реализации предпочтут по умолчанию построить объекты n
внутри вектора, так как построение копии может быть недоступно. Это было бы изменением в этом случае.
Мой вопрос:
- Указывает ли стандарт С++ 03, что
std::vector
должен иметь конструктор, определенный выше, т.е. с аргументом по умолчанию? В частности, есть ли гарантия того, что записи векторного объекта будут скопированы вместо построенного по умолчанию?
- Что говорит стандарт С++ 11 об этой же точке?
- Я рассматриваю это как возможность для разрыва между С++ 03 и C + 11. Рассмотрена ли эта проблема? Решено?
PS: Пожалуйста, никаких комментариев о конструкторе по умолчанию для класса S
выше. Это было или реализовано в какой-то форме ленивой конструкции.
Ответы
Ответ 1
Предоставляет ли стандартное условие С++ 03, что std::vector
должен иметь конструктор, определенный выше, т.е. с аргументом по умолчанию? В частности, есть ли гарантия того, что записи векторного объекта будут скопированы, а не построены по умолчанию?
Да, указанное поведение состоит в том, что x
копируется n
раз, чтобы инициализировать контейнер, содержащий элементы n
, которые являются всеми копиями x
.
Что говорит стандарт С++ 11 об этой же точке?
В С++ 11 этот конструктор был превращен в два конструктора.
vector(size_type n, const T& x, const Allocator& = Allocator()); // (1)
explicit vector(size_type n); // (2)
За исключением того факта, что у него больше нет аргумента по умолчанию для второго параметра, (1) работает так же, как в С++ 03: x
копируется n
раз.
Вместо аргумента по умолчанию для x
добавлен (2). Это значение конструктора инициализирует элементы n
в контейнере. Копии не копируются.
Если вам требуется старое поведение, вы можете гарантировать, что (1) вызывается путем предоставления второго аргумента вызову конструктора:
std::vector<S> v(42, S());
Я рассматриваю это как возможность для разрыва между С++ 03 и С++ 11. Я рассматриваю это как возможность для разрыва между С++ 03 и С++ 11. Рассмотрена ли эта проблема? Решено?
Да, как показывает ваш пример, это действительно потрясающее изменение.
Поскольку я не являюсь членом комитета по стандартизации С++ (и я не уделял особо пристального внимания библиотечным материалам в рассылках), я не знаю, в какой степени обсуждалось это нарушение.
Ответ 2
Я думаю, что решение для используемого вами варианта использования не является оптимальным и не полным, поэтому у вас возникли проблемы с обновлением до С++ 11.
С++ всегда заботится о семантике, и когда вы пишете программу на С++, вам лучше понять вашу семантику. Поэтому в вашем случае вы хотите создать N объектов, но пока вы их не меняете, вы хотите, чтобы они использовали одну и ту же память для оптимизации. Хорошая идея, но как это сделать:
1) конструктор копирования.
2) статическая реализация + конструктор копирования.
Вы рассматривали оба решения?
Предположим, вам нужны M-векторы из N объектов, сколько раз разделяемая память будет выделена, если вы выберете 1-й сценарий? Это M, но зачем нам выделять память M раз, если мы хотим создать векторы, содержащие объекты MxN?
Таким образом, правильная реализация заключается в том, чтобы указывать на статическую память по умолчанию и выделять память только при изменении объекта. В таком случае выделение M векторов из N объектов даст вам... 1 'разделяемое' распределение памяти.
В вашем случае вы нарушили правильный семантический злоупотребляющий конструктор копирования, который:
1) не очевидно
2) не является оптимальным
и теперь вам нужно окупиться.