Мне нужен максимальный размер моего вектора (значение, которое я дал) C ++ - PullRequest
0 голосов
/ 02 мая 2018

У меня есть следующие коды:

Фиксированный размер вектора:

template <typename T, size_t siz>
struct fixVec : public std::vector<T> {
    void push_back(const T& d) {
    if (std::vector<T>::size() >= siz) throw std::bad_alloc();
    std::vector<T>::push_back(d);
}
};

Я использую "fixVec" следующим образом:

ClonableHeteroStore<ClonableTestClass, fixVec<ClonableTestClass*, 1>, MyException> t1;

Создает вектор размером 1. (1)

EXPECT_NO_THROW(t1.add(new ClonableTestClass));

Добавляет 1 элемент, это нормально, без ошибок. (2)

EXPECT_THROW(t1.add(new ClonableTestClass), MyException);

Здесь следует выбросить исключение. (3)

Моя проблема заключается в следующем: как я могу проверить, когда я достигаю "max_size ()" моего вектора. Не максимальный размер вектора, который может обрабатывать мой компьютер. Или как я могу вернуть значение, которое я дал в коде (1) (число между!):

ClonableHeteroStore<ClonableTestClass, fixVec<ClonableTestClass*, !!!1!!! >, MyException> t1;

У меня есть класс с именем ClonableHeteroStore, где у меня есть следующий код:

template <typename T1, class S, typename PRED = int>
class ClonableHeteroStore 
{
private:
    S flakker;
    size_t currently;
    size_t maxi;
public:
    ClonableHeteroStore() :  { currently = 0; maxi = 0; }

    void add(T1 *rhs)
    {
        if (currently < ???????? )
        {
            flakker.push_back(rhs);
            currently++;
        }
        else
        {
            delete rhs;
            throw PRED();
        }
    }

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

Таким образом, строка (2) не будет иметь никаких проблем, потому что размер вектора равен 1, и это первый элемент, который мы вставили в него. Часть «если» должна выполняться.

Строка (3) сгенерирует исключение, поскольку она пытается поместить второй элемент, но размер вектора равен 1 . Часть else должна выполняться.

Единственный код, который можно изменить, это класс ClonableHeteroStore. Каким-то образом я должен выяснить, насколько велик мой вектор. Я много читал, что вектор динамически выделяет свой размер и т. Д. И т. Д., Но в этом случае он должен выдать исключение, если я перепрыгну через предел (1).

Спасибо за любую помощь!

Ответы [ 3 ]

0 голосов
/ 02 мая 2018

Интересный вопрос!

Например, вы можете использовать параметры шаблона шаблона :

template<template<typename, size_t> typename X, typename T, size_t N>
constexpr size_t get_size(const X<T, N>&) {
    return N;
}

int main() {
    std::cout << get_size(fixVec<int, 5>{}) << std::endl;
}

Это будет работать не только для fixVec, но и для любого типа шаблона с типом и size_t в качестве аргументов шаблона!

Так, например, следующее также верно:

get_size(std::array<int, 5>{});
0 голосов
/ 02 мая 2018

Фиксированный размер вектора:

Почему вы используете std::vector вместо std:array для "фиксированного" массива?

Создает вектор размером 1. (1)

Нет, это не так. Он создает vector, чьи size() и capacity() изначально равны 0, а не 1. Ваше значение siz используется как искусственная максимальная емкость , вы все еще позволяете вектору динамически расти пока он не достигнет вашего максимума.

EXPECT_THROW(t1.add(new ClonableTestClass), MyException);

Здесь следует выбросить исключение. (3)

Правда и, что еще хуже, в результате происходит утечка объекта ClonableTestClass. Вместо этого вам следует использовать std::unique_ptr<ClonableTestClass>, чтобы вы всегда владели объектом и правильно его освобождали, даже если выдается исключение.

как я могу проверить, когда я достигну "max_size ()" моего вектора.

Вы уже есть. Это именно то, что делает size() >= siz.

Или как мне вернуть значение, которое я дал в коде (1) (число между!):

ClonableHeteroStore<ClonableTestClass, fixVec<ClonableTestClass*, !!!1!!! >, MyException> t1;

Доступно только в параметре шаблона siz. Если вы хотите, чтобы ВНЕШНИЙ код знал значение siz, то вам необходимо предоставить к нему публичный доступ, например:

template <typename T, size_t siz>
struct fixVec : public std::vector<T>
{
    const size_t max_elements_allowed;
    fixVec() : max_elements_allowed(siz) {}
    ...
};

Или, если вы используете C ++ 11 или более позднюю версию:

template <typename T, size_t siz>
struct fixVec : public std::vector<T>
{
    const size_t max_elements_allowed = siz;
    ...
};

Тогда вы можете сделать это:

void add(T1 *rhs)
{
    if (currently < flakker.max_elements_allowed)
    {
        flakker.push_back(rhs);
        currently++;
    }
    else
    {
        delete rhs;
        throw PRED();
    }
}

К вашему сведению, вам на самом деле не нужно currently, вы можете просто использовать flakker.size() вместо:

void add(T1 *rhs)
{
    if (flakker.size() < flakker.max_elements_allowed)
    {
        flakker.push_back(rhs);
    }
    else
    {
        delete rhs;
        throw PRED();
    }
}

Тем не менее, std::vector имеет метод capacity(), который вы действительно ищете:

void add(T1 *rhs)
{
    if (flakker.size() < flakker.capacity())
    {
        flakker.push_back(rhs);
    }
    else
    {
        delete rhs;
        throw PRED();
    }
}

Вам просто нужно предварительно выделить внутренний массив vector заранее:

template <typename T, size_t siz>
struct fixVec : public std::vector<T>
{
    fixVec() { std::vector<T>::reserve(siz); } // <-- add this!
    ...
};

У меня есть класс с именем ClonableHeteroStore, где у меня есть следующий код:

...

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

Поскольку ваш fixVec уже обрабатывает это, вы должны просто добавить элемент безоговорочно и позволить push_back() throw при необходимости:

void add(T1 *rhs)
{
    try
    {
        flakker.push_back(rhs);
    }
    catch (...)
    {
        delete rhs;
        throw PRED();
    }
}

Лучшее решение - позволить вызывающей стороне сохранять право собственности на добавляемый объект и не освобождать эту собственность, если add() не будет успешным. Таким образом, если выдается исключение, вызывающая сторона может правильно освободить объект и не пропустить его. Это не должно быть обязанностью add():

void add(T1 *rhs)
{
    try
    {
        flakker.push_back(rhs);
    }
    catch (...)
    {
        throw PRED();
    }
}

...

ClonableTestClass *obj = new ClonableTestClass;
try
{
    t1.add(obj);
}
catch (...)
{
    delete obj;
    throw;
}

Или, если вы используете C ++ 11 или более позднюю версию:

std::unique_ptr<ClonableTestClass> obj(new ClonableTestClass);
t1.add(obj.get());
obj.release();

Или:

void add(std::unique_ptr<T1> rhs)
{
    try
    {
        flakker.push_back(rhs.get());
        rhs.release();
    }
    catch (...)
    {
        throw PRED();
    }
}

...

t1.add(std::unique_ptr<ClonableTestClass>(new ClonableTestClass));

Единственный код, который можно изменить, это класс ClonableHeteroStore. Каким-то образом я должен выяснить, насколько велик мой вектор.

Если вы не можете изменить fixVec, чтобы сделать его значение siz общедоступным, и вы должны выяснить его siz, то вам придется прибегнуть к некоторой хитрости шаблона, например:

template<typename T, size_t siz>
size_t get_capacity(const fixVec<T, siz> &) { return siz; }

...

void add(T1 *rhs)
{
    if (flakker.size() < get_capacity(flakker))
        flakker.push_back(rhs);
    else
        throw PRED();
}

Но именно поэтому контейнеры STL предоставляют стандартные методы size() и capacity(), поэтому вам не нужно прибегать к подобным приемам.

С другой стороны, ClonableHeteroStore на самом деле не нужно знать это значение siz для начала. Он должен просто выполнить push_back() безоговорочно и позволить ему бросить, если необходимо.

Я много читал, что вектор динамически выделяет свой размер и т. Д. И т. Д., Но в этом случае он должен выдать исключение, если я перепрыгну через предел (1).

Вы действительно должны использовать std::array вместо std::vector.

Если вы должны использовать std::vector, то вам следует подумать о написании пользовательского Allocator для использования vector вместо использования по умолчанию Allocator. Пользовательский Allocator может выдать, если его просят выделить память для слишком большого количества элементов. Тогда вы можете позволить std::vector вести себя нормально и вообще не переопределять его push_back().

0 голосов
/ 02 мая 2018

Два общих решения для извлечения аргументов шаблона из типа - это черты типа, которые используют частичную явную специализацию или используют шаблон функции для вывода аргументов шаблона из аргумента функции. Функциональный подход состоит в написании функции, которая ожидает fixVec<T, I>, и получении компилятором вывода I:

// The compiler will deduce T and I from the argument
template<class T, size_t I>
size_t get_vec_size(const fixVec<T, I> &) {
    return I;
}

void foo()
{
    fixVec<char, 10> my_vec;
    const auto my_vec_max_size = get_vec_size(my_vec);
}

Чтобы использовать частичную явную специализацию, вы пишете новый шаблон и специализируете его для типа, аргументы шаблона которого вам нужны, и снова получаете компилятор для вывода этих аргументов для вас:

template<class T>
struct vec_size {};

template<class T, size_t I>
struct vec_size<fixVec<T, I>> : std::integral_constant<size_t, I> {};

void foo()
{
    fixVec<char, 10> my_vec;
    const auto my_vec_max_size = vec_size<decltype(my_vec)>::value;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...