Подтвердить что ты не робот

Как я могу инициализировать std::vector с параметром размера и каждый из них был построен независимо?

Мне нравится создавать векторы с заданным размером и значением, например:

std::vector<std::string> names(10);

Однако это несколько раз приводило к неожиданным результатам. Например, в следующем коде каждый UniqueNumber оказывается одинаковым:

#include <iostream>
#include <string>
#include <vector>


struct UniqueNumber
{
    UniqueNumber() : mValue(sInstanceCount++)
    {
    }

    inline unsigned int value() const
    {
        return mValue;
    }

private:
    static unsigned int sInstanceCount;
    unsigned int mValue;
};


int UniqueNumber::sInstanceCount(0);


int main()
{
    std::vector<UniqueNumber> numbers(10);
    for (size_t i = 0; i < numbers.size(); ++i)
    {
        std::cout << numbers[i].value() << " ";
    }
}

Выход консоли:

0 0 0 0 0 0 0 0 0 0

Это имеет смысл при взгляде на конструктор std::vector:

explicit vector(size_type __n,
                const value_type& __value = value_type(),
                const allocator_type& __a = allocator_type());

По-видимому, вектор был инициализирован копиями одного и того же объекта.

Есть ли способ создания каждого объекта по умолчанию?

4b9b3361

Ответ 1

generate или generate_n предназначены специально для того, что вы хотите сделать. Вот полный пример:

#include <algorithm>
#include <vector>
#include <iterator>
#include <iostream>
using namespace std;

class Foo
{
public:
    Foo() : n_(++water_) {};
    operator unsigned () const { return n_; }
private:
    static unsigned water_;
    unsigned n_;
};

Foo make_foo()
{
    return Foo();
}

unsigned Foo::water_ = 0;

int main()
{
    vector<Foo> v_foo;
    generate_n(back_inserter(v_foo), 10, &make_foo);
    copy(v_foo.begin(), v_foo.end(), ostream_iterator<unsigned>(cout, " "));
}

Выход: 1 2 3 4 5 6 7 8 9 10

Ответ 2

Вектор - это копирование элементов для инициализации.

Попробуйте следующее:

#include <iostream>
#include <string>
#include <vector>


struct UniqueNumber
{
    UniqueNumber(bool automatic = true) 
        : mValue(automatic?sInstanceCount++:Special)
    { }

    UniqueNumber(UniqueNumber const& other) 
        : mValue(other.mValue==Special?sInstanceCount++:other.mValue)
    { }

    unsigned int value() const
    {
        return mValue;
    }

private:
    static int sInstanceCount;
    unsigned int mValue;
    static unsigned int const Special = ~0U;
};


int UniqueNumber::sInstanceCount(0);


int main()
{
    std::vector<UniqueNumber> numbers(10,UniqueNumber(false));
    for (size_t i = 0; i < numbers.size(); ++i)
    {
        std::cout << numbers[i].value() << " ";
    }
}

Ответ 3

Есть два способа сделать это.

Хороший и С++ 0x способ состоит в том, чтобы воспользоваться списком инициализаторов:

std::vector<UniqueNumber> vec = { UniqueNumber(0), UniqueNumber(1) };

Очевидно, проблема здесь в том, что вы должны указать их полностью.

В С++ 03 я просто использовал бы цикл:

std::vector<UniqueNumber> vec;
vec.reserve(10);
for (size_t i = 0; i != 10; ++i) { vec.push_back(UniqueNumber(i)); }

Конечно, это может прекрасно встраиваться в функцию построителя:

template <typename ValueType, typename Generator>
std::vector<ValueType> generateVector(size_t size, Generator generator)
{
  std::vector<ValueType> vec;
  vec.reserve(size);
  for (size_t i = 0; i != size; ++i) { vec.push_back(generator()); }
  return vec;
}

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

То же самое можно сделать с STL generate_n в <algorithm>, и я включу ощущение синтаксиса лямбда.

std::vector<ValueType> vec;
size_t count = 0;
std::generate_n(std::back_inserter(vec), 10, [&]() { return Foo(++count); });

Предлагается @Eugen в комментариях:)


Примечание: wrt, чтобы "удивить", если вы хотите иметь уникальные элементы, возможно, что vector не является наиболее подходящей структурой данных. И если вам действительно нужен vector, я бы рассмотрел возможность обертывания в класс, чтобы обеспечить в качестве инварианта уникальные элементы.

Ответ 4

Вам нужно добавить конструктор копирования:

UniqueNumber(const UniqueNumber& un) : mValue(sInstanceCount++)
{ }

Конструктор заполнения std::vector не вызывает ваш конструктор по умолчанию. Скорее, он вызывает конструктор копии по умолчанию, который существует неявно. Разумеется, этот конструктор не будет увеличивать вашу внутреннюю статическую переменную счетчика.

Вам также необходимо определить оператор присваивания.

Однако использование объекта с внутренним статическим счетчиком с std::vector приведет к неожиданным результатам, поскольку вектор может внутренне копировать/конструировать/назначать ваш объект столько, сколько сочтет нужным. Таким образом, это может помешать вам семантике копирования.