Использовать шаблон для преодоления отсутствия базового класса? - PullRequest
5 голосов
/ 12 августа 2011

Очевидно, что стандартные контейнеры не имеют ни общего базового класса, ни общего интерфейса, хотя имена методов являются однородными.

Проблема: я должен заполнить контейнер коллекцией объектов уникального типа. Контейнером может быть std::list, std::vector или std::deque и, возможно, какой-либо другой пользовательский контейнер. Является ли следующий код лучшим решением?

# include <string>
# include <iostream>
# include <list>
# include <vector>
# include <deque>

/*
 * Fill a container with two strings. The container
 * must expose the `clear` and `push_back` methods.
 */
template<typename T>
void f(T & t)
{
    t.clear() ;

    t.push_back("Alice") ;
    t.push_back("Bob") ;
}

int main(int, char*[])
{
    std::list<std::string>    l ;
    std::vector<std::string>  v ;
    std::deque<std::string>   q ;

    f(l) ;   // fill the list
    f(v) ;   // fill the vector
    f(q) ;   // fill the double-ended queue

    // possibly anything with `clear` and `push_back` methods
    // can be filled with `f`

    return 0 ;
}

Спасибо за любой совет!


EDIT

Вот случай, который я проиллюстрировал f в моем первом посте:

struct AudioFormat
{
    uint32_t   samplerate ;   // Sampling frequency
    uint8_t    channels ;     // The number of channels
    uint8_t    bitdepth ;     // The number of bits per sample
} ;

class AudioDevice
{
    // many stuff skipped
    public :
      /*
       * Fills the container with available audio formats handled properly by the device
       */
      void GetSupportedAudioFormats(std::list<AudioFormat> &) ;
    // many stuff skipped
} ;

Я ищу лучший способ объявить GetSupportedFormats, чтобы он мог обрабатывать многие другие контейнеры, не только std::list s. В этом смысл моего первого поста.

Ответы [ 3 ]

6 голосов
/ 12 августа 2011

Мой фаворит будет:

/*
 * Fill a container with two strings. The container
 * must expose the `clear` and `push_back` methods.
 */
template<typename T>
void f(T & t)
{
    t.clear() ;

    std::insert_iterator<T> it(t, t.end());
    *it++ = "Alice";
    *it++ = "Bob";
}

Ограничения теперь: clear и insert, поэтому он также будет работать с std::set, например. Кроме того, он может работать с любым типом, вам просто нужно будет специализировать шаблон std::insert_iterator для него.

3 голосов
/ 12 августа 2011

Это одно решение.

Более подходящее решение в стиле "STL" - это использование std::back_inserter

char const* names[2] = { "Alice", "Bob" };

std::list<std::string> l;
std::vector<std::string> v;
std::deque<std::string> q;  

std::copy(names, names+2, std::back_inserter(l));
std::copy(names, names+2, std::back_inserter(v));
std::copy(names, names+2, std::back_inserter(q));
1 голос
/ 12 августа 2011

Предоставьте свой собственный абстрактный класс с методами clear () и add ()

class ContainerIterface
{
public:
  virtual void clear() = 0;
  virtual void add(const UniqueType &e) = 0;
};

и тогда вы можете извлечь из него что-то вроде

template <typename Container>
class TemplatedContainer : public ContainerIntarface {
  virtual void clear() {c_.clear();}
  virtual void add(const UniqueType &e) {std::inserter(c_, c_.end()) = e;}

private:
  Container c_;
};

Это предполагает, что у вас есть только один тип для хранения в контейнере. Если это не так, ваш базовый класс тоже становится temaplate, а производный класс требует аргумент шаблона, который является шаблоном (std::vector вместо std::vector<UniqueType>)

...