std::vector, конструкция по умолчанию, C++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 скопированы с него.

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

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

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

Мой вопрос:

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

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

2 ответа

Решение

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

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


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

В 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) был добавлен. Этот конструктор инициализирует значение n элементы в контейнере. Копии не сделаны.

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

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

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

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

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

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

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

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

Поэтому правильной реализацией здесь является указание на статическую память по умолчанию и выделение памяти только в случае изменения объекта. В таком случае выделение M векторов из N объектов даст вам... 1 "общее" выделение памяти.

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

Другие вопросы по тегам