Typedef и оператор ostream для std::vector

Я создал класс Chromosome, который оказался просто оболочкой для вектора с оператором ostream, поэтому вместо этого я решил использовать вектор typedef. Однако у меня проблемы с шаблонным оператором ostream... Это лучший способ сделать это? (Я видел несколько подходов и не смог заставить их работать)

template<typename G>
class Chromosome {
 public:
  typedef typename std::vector<G> type;
  typedef typename std::pair<type *,type *> ptr_pair;
};

template<typename G> //line 19 below:
std::ostream& operator<<(std::ostream& os, const Chromosome<G>::type& chromosome) {
  for(auto iter = chromosome.begin(); iter != chromosome.end(); ++iter)
    std::cout << *iter;
  return os;
}

На данный момент ошибка, которую я получаю:

chromosome.h:19: error: expected unqualified-id before ‘&’ token
chromosome.h:19: error: expected ‘)’ before ‘&’ token
chromosome.h:19: error: expected initializer before ‘&’ token

Ваше здоровье.


person Rhys van der Waerden    schedule 12.02.2011    source источник


Ответы (2)


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

template<typename G>
std::ostream& operator<<(std::ostream& os, const typename Chromosome<G>::type& chromosome);

Причина в том, что если бы вы специализировали Chromosome для разных типов, вы могли бы оказаться в ситуации, когда компилятор не мог бы однозначно вывести G. Например:

template <typename G> class Chromosome {
public:
    typedef std::vector<G> type; // No typename needed here, BTW
};

template <> class Chromosome<int> {
public:
    typedef std::vector<double> type;
};

Теперь, что произойдет, если вы сделаете это?

vector<double> v;
cout << v << endl;

Компилятор не может сказать, является ли G double или int в этом случае, потому что и Chromosome<int>, и Chromosome<double> имеют vector<double> в качестве вложенного типа.

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

template<typename G>
std::ostream& operator<<(std::ostream& os, const std::vector<G>& chromosome);

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

person templatetypedef    schedule 12.02.2011
comment
Спасибо, теперь я понял, как это работает. использование вектора совершенно нормально, так как я пытался использовать Chromosome‹G›::type только для удобства чтения. Не могли бы вы объяснить, что здесь не требуется имя типа? - person Rhys van der Waerden; 13.02.2011

Член typedef type является зависимым именем: его значение зависит от параметра шаблона G. Вам нужно использовать typename, чтобы сообщить компилятору, что type называет тип:

const typename Chromosome<G>::type&

Полное объяснение можно найти в статье часто задаваемых вопросов о переполнении стека C++, Где разместить "шаблон" и "имя типа" для зависимых имен.

Как намекает @templatetypedef в комментариях, хотя это позволит скомпилировать код, он не будет «работать», чтобы позволить вам вставить std::vector<G> в std::ostream, потому что type находится в невыведенном контексте.

Самый простой способ объявить перегрузку и получить ожидаемое поведение — использовать std::vector<G> непосредственно в качестве типа аргумента:

template<typename G>
std::ostream& operator<<(std::ostream& os, const std::vector<G>& chromosome)
person James McNellis    schedule 12.02.2011
comment
На самом деле это не сработает, потому что компилятор не может вывести G в этом контексте. Отчасти это связано с тем, что при специализации шаблона могут возникнуть ситуации, когда компилятор не может знать, какой тип использовать. - person templatetypedef; 12.02.2011
comment
@templatetypedef: Хороший вопрос; зависимое имя — не единственная проблема. - person James McNellis; 12.02.2011
comment
Спасибо за ответ, я нашел, что templatetypedef немного понятнее, поэтому он получает этот. - person Rhys van der Waerden; 13.02.2011