std :: vector, конструкция по умолчанию, C ++ 11 и критические изменения - PullRequest
52 голосов
/ 22 апреля 2011

Сегодня я столкнулся с довольно тонкой проблемой, о которой я хотел бы узнать ваше мнение.

Рассмотрим следующий класс идиомы для общего вида сада:

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.

Теперь в C ++ 11 есть ссылки на rvalue. Так что это не может работать так. Если vector построен как

std::vector<S> v(42);

тогда, скорее всего, реализации выберут конструкцию по умолчанию n объектов внутри вектора, так как копирование может быть недоступно. Это было бы серьезным изменением в этом случае.

Мой вопрос:

  1. Обязывает ли стандарт C ++ 03, что std::vector должен иметь конструктор, определенный выше, т.е. с аргументом по умолчанию? В частности, есть ли гарантия, что записи векторного объекта будут скопированы вместо созданного по умолчанию?
  2. Что стандарт C ++ 11 говорит об этом же пункте?
  3. Я вижу в этом возможность переломного изменения между C ++ 03 и C + 11. Была ли исследована эта проблема? Решено?

PS: Пожалуйста, не комментируйте конструктор по умолчанию класса S выше. Это было или реализация какой-то ленивой конструкции.

Ответы [ 2 ]

46 голосов
/ 22 апреля 2011

Требует ли в стандарте C ++ 03, что std::vector должен иметь конструктор, определенный выше, то есть с аргументом по умолчанию?В частности, есть ли гарантия, что записи векторного объекта будут скопированы, а не созданы по умолчанию?

Да, указанное поведение таково, что x копируется n раз, так что контейнер инициализируется так, чтобы в нем содержалось n элементов, которые все являются копиями x.


Что стандарт C ++ 11 говорит об этом же пункте?

В C ++ 11 этот конструктор был превращен в два конструктора.

vector(size_type n, const T& x, const Allocator& = Allocator()); // (1)
explicit vector(size_type n);                                    // (2)

За исключением того факта, что у него больше нет аргумента по умолчанию для второго параметра, (1) работает так же, как и в C ++ 03: x копируется n раз.

Вместо аргумента по умолчанию для x, (2) был добавлен.Этот конструктор value-инициализирует n элементов в контейнере.Копии не создаются.

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

std::vector<S> v(42, S());

Я вижу в этом возможность прорыва между C ++ 03 и C ++ 11.Я вижу в этом возможность переломного изменения между C ++ 03 и C ++ 11.Была ли исследована эта проблема?Решено?

Да, как показывает ваш пример, это действительно серьезное изменение.

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

0 голосов
/ 02 марта 2012

Я думаю, что решение для описанного вами варианта использования не является оптимальным и неполным, поэтому у вас возникают проблемы при обновлении до C ++ 11.

C ++ всегда заботится о семантике, и когда вы пишете программу на c ++, вылучше бы понять вашу семантику.Таким образом, в вашем случае вы хотите создать N объектов, но пока вы не меняете их, вы хотите, чтобы они совместно использовали одну и ту же память для оптимизации.Хорошая идея, но как это сделать: 1) Копировать конструктор.2) статическая реализация + конструктор копирования.Рассматривали ли вы оба решения?

Считаете ли вы, что вам нужно M векторов из N объектов, сколько раз будет распределена общая память, если вы выберете 1-й сценарий?Это M, но зачем нам выделять память M раз, если мы хотим создать векторы, содержащие объекты MxN?

Итак, правильная реализация здесь - указывать на статическую память по умолчанию и выделять память только в случае изменения объекта.В таком случае выделение M векторов из N объектов даст вам ... 1 «общее» выделение памяти.

В вашем случае вы нарушили правильный конструктор копий семантического злоупотребления, который: 1) неочевиден 2) неоптимален и теперь вы должны расплатиться.

...