C++, как объявить самоопределенный массив в универсальном программировании

У меня есть шаблон класса

template <class T> class Collection
{
private:
    int size;
    int type;
    T* Arr;
    int Case;

public:

void ArrayGenerating() {
    switch(type) {
    case 1: 
        Arr = new T[size];

        for (int i = 0; i < size; i++) {
            srand((unsigned)time(NULL)); 
            Arr[i] = static_cast <T> (rand()) % size;
        }

    case 2:
        Arr = new T[size];

        for (int i = 0; i < size; i++) {
            srand((unsigned)time(NULL)); 
            Arr[i] = static_cast <T> (rand()) / (static_cast <T> (RAND_MAX/size));
        }

    case 3:
        Arr = new T[size];

        for (int i = 0; i < size; i++) {
            srand((unsigned)time(NULL)); 
            Arr[i].setNumerator(static_cast <int> (rand()) % size);

            srand((unsigned)time(NULL));
            Arr[i].setDenominator(static_cast <int> (rand()) % size);
        }

    }
  }
};

Я хочу создать случайный массив общего типа данных

с типом 1, это массив целых чисел. тип 2, массив с плавающей точкой. Тип 3, у меня есть определенный тип данных "дробь". Но когда я компилирую программу, возникают ошибки:

Error   1   error C2228: left of '.setNumerator' must have class/struct/union

Error   2   error C2228: left of '.setDenominator' must have class/struct/union

Так есть ли какие-либо решения для этого осложнения?

3 ответа

Решение

Похоже, type является константой, зависящей от T. В противном случае не имеет смысла указывать точку T* на int, когда T - это число с плавающей точкой. Если это правда, в этом нет необходимости.

Я думаю, что вы ищете специализацию шаблона (непроверенный код):

// this is common to all cases.
class CollectionBase {
  protected:
    int size;
};

// the general template is not defined
// the compiler will complain whenever T is neither int, nor float, nor fraction.
template<class T> class Collection;

// here come the specializations
template<> class Collection<int>: private CollectionBase
{
  private:
    int* Arr;    
  public:
    void ArrayGenerating() {
      Arr = new int[size];
      for (int i = 0; i < size; i++) {
        srand((unsigned)time(NULL)); 
        Arr[i] = static_cast<int>(rand()) % size;
      }
    }
};

template<> class Collection<float>: private CollectionBase
{
  private:
    float* Arr;
  public:
    void ArrayGenerating() {
      Arr = new float[size];

      for (int i = 0; i < size; i++) {
          srand((unsigned)time(NULL)); 
          Arr[i] = static_cast<float>(rand()) / (static_cast<float>(RAND_MAX/size));
      }
    }
};

template<> class Collection<fraction>: private CollectionBase
{
  private:
    fraction* Arr;
  public:
    void ArrayGenerating() {
      Arr = new fraction[size];

      for (int i = 0; i < size; i++) {
          srand((unsigned)time(NULL)); 
          Arr[i].setNumerator(static_cast <int> (rand()) % size);

          srand((unsigned)time(NULL));
          Arr[i].setDenominator(static_cast <int> (rand()) % size);
      }
    }
};

Обратите внимание, что этот вид кода опасен. Рассматривать std::vector<> вместо того, чтобы управлять динамически размещенным массивом самостоятельно.

Также помните, что, как правило, все методы вашего класса должны безопасно вызываться, как только конструктор завершит работу. В вашем коде любая функция, которая обращается Arr использует случайный указатель на некоторую память, прежде чем ArrayGenerating() побежал. Всякий раз, когда вы звоните ArrayGenerating() дважды по какой-то причине ваш код будет пропускать память, потому что вы никогда не удосужились delete[] Ваш массив перед созданием нового.

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

Итак, вот полное (пока не проверенное) наиболее общее решение для вас. Я бы начал с бесплатной функции для создания случайных чисел:

template<typename T> struct dist{
    using uniform = std::uniuniform_int_distribution<T>;
};
template<> struct dist<float> {
    using uniform = std::uniuniform_real_distribution<float>;
};

template<typename T>
std::vector<T> createRandomNumbers(size_t s) {
    auto e1 = std::default_random_engine{std::random_device{}()};
    auto u = dist<T>::uniform{0, static_cast<T>(s)};

    auto r = std::vector<T>(s, 0);
    for( auto& i: r ) i = u(e1);

    return r;
}
// fraction need a specialization
template<>
std::vector<fraction> createRandomNumbers<fraction>(size_t s) {
    auto e1 = std::default_random_engine{std::random_device{}()};
    auto u = dist<int>::uniform{0, static_cast<int>(s)};

    auto r = std::vector<fraction>(s, 0);
    for( auto& i: r ) {
          i.setNumerator(u(e1));
          i.setDenominator(u(e1));
    }

    return r;
}

Теперь мы реализуем Collection Шаблон класса, как ваш, если он нам действительно нужен:

template <typename T> Collection {
    private:
        // this will handle all your memory management needs
        std::vector<T> randoms;
    public:
        Collection(size_t s) :
            randoms{createRandomNumbers<T>(s)}
        {};

        createNewRandoms(size_t s) {
            std::swap(randoms, createRandomNumbers<T>(s));
        };
        // whatever else is necessary
};

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

#include <iostream>
#include <chrono>
#include <random>

template<class type_t, std::size_t size>
class Array
{
private:
    type_t arr[size];
public:
    Array()
    {
        for (std::size_t i = 0; i < size; ++i)
        {
            //nice C++ random number generation
            auto seed = static_cast<unsigned>(std::chrono::system_clock::now().time_since_epoch().count());
            std::minstd_rand0 randm(seed);

            arr[i] = randm();
        }
    }

    //test function
    void print()
    {
        for (int i = 0; i < size; ++i)
            std::cout << arr[i] << " ";
    }
};

int main() 
{
    Array<int, 4> arr;
    arr.print();

    std::cin.get();
}

Попробуйте убежать от C-стиля C++. Присоединяйтесь к темной стороне.

Примечание: я не буду комментировать использование вами функций C или другие проблемы. Другие уже говорили вам, как их избежать.


type член в шаблоне класса как-то побеждает цель общего программирования, не так ли? Вы должны избавиться от своего type и заменить switch с шаблонной специализацией.

Вот простой пример для начала:

// Collection for all T except of `fraction`
template <class T> class Collection
{
private:
    int size;
    T* Arr;
    int Case;

public:
    void ArrayGenerating() {
        Arr = new T[size];

        for (int i = 0; i < size; i++) {
            srand((unsigned)time(NULL)); 
            Arr[i] = static_cast <T> (rand()) % size;
        }
    };
};

// Collection for `fraction`
template <> class Collection<fraction>
{
private:
    int size;
    fraction* Arr;
    int Case;

public:
    void ArrayGenerating() {
        Arr = new fraction[size];

        for (int i = 0; i < size; i++) {
            srand((unsigned)time(NULL)); 
            Arr[i].setNumerator(static_cast <int> (rand()) % size);

            srand((unsigned)time(NULL));
            Arr[i].setDenominator(static_cast <int> (rand()) % size);
        }
    }
};

Это самый простой вид специализации шаблонов, но он может привести к значительному дублированию кода. Вы также можете обойти эту проблему. Например, вы можете извлечь все общие части в общий частный базовый класс, что-то вроде этого:

namespace detail
{
    template <class T> class CollectionBase
    {
    protected:
        int size;
        T* Arr;
        int Case;
    };
}

template <class T> class Collection : detail::CollectionBase<T>
{
public:
    void ArrayGenerating() {
        Base::Arr = new T[Base::size];

        for (int i = 0; i < Base::size; i++) {
            srand((unsigned)time(NULL)); 
            Base::Arr[i] = static_cast <T> (rand()) % Base::size;
        }
    };
private:
    using Base = detail::CollectionBase<T>;
};

template<> class Collection<fraction> : detail::CollectionBase<fraction>
{
public:
    void ArrayGenerating() {
        Base::Arr = new fraction[Base::size];

        for (int i = 0; i < size; i++) {
            srand((unsigned)time(NULL)); 
            Arr[i].setNumerator(static_cast <int> (rand()) % size);

            srand((unsigned)time(NULL));
            Arr[i].setDenominator(static_cast <int> (rand()) % size);
        }
    }
private:
    using Base = detail::CollectionBase<fraction>;
};

Как правило, читайте больше о специализации шаблонов, и вы обязательно найдете правильное решение:

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