Operador Typedef e ostream para um std::vector
Pergunta
Criei uma classe Chromosome que acabou sendo simplesmente um wrapper para vector com um operador ostream, então decidi digitar vector em vez disso.No entanto, estou tendo problemas com o operador ostream modelado...Esta é a melhor maneira de fazer isso?(Vi algumas abordagens e não consegui fazer nenhuma funcionar)
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;
}
No momento o erro que estou recebendo é:
chromosome.h:19: error: expected unqualified-id before ‘&’ token
chromosome.h:19: error: expected ‘)’ before ‘&’ token
chromosome.h:19: error: expected initializer before ‘&’ token
Saúde.
Solução
Infelizmente, não há uma maneira limpa de fazer isso porque o compilador não consegue deduzir o tipo de G
da declaração da função
template<typename G>
std::ostream& operator<<(std::ostream& os, const typename Chromosome<G>::type& chromosome);
A razão é que se você se especializar Chromosome
para tipos diferentes, você pode acabar em uma situação em que o compilador não poderá inferir inequivocamente G
.Por exemplo:
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;
};
Agora, o que aconteceria se você fizesse isso?
vector<double> v;
cout << v << endl;
O compilador não pode dizer se G
é double
ou int
neste caso, porque ambos Chromosome<int>
e Chromosome<double>
ter vector<double>
como seu tipo aninhado.
Para corrigir isso, você terá que usar explicitamente o tipo vector<G>
como o argumento:
template<typename G>
std::ostream& operator<<(std::ostream& os, const std::vector<G>& chromosome);
Infelizmente, realmente não há uma maneira melhor de fazer isso.Não é realmente um defeito na linguagem, já que há uma boa razão para proibi-lo, mas na verdade impede você de fazer o que deseja neste contexto.
Outras dicas
O membro typedef type
é um nome dependente:seu significado depende do parâmetro do modelo G
.Você precisa usar um typename
para dizer ao compilador que type
nomeia um tipo:
const typename Chromosome<G>::type&
Para obter a explicação completa, considere a leitura do artigo Stack Overflow C++ FAQ, Onde colocar o “template” e o “typename” nos nomes dependentes.
Como @templatetypedef alude nos comentários, embora isso permita a compilação do código, não "funcionará" para permitir que você insira um std::vector<G>
em um std::ostream
porque type
está em um contexto não deduzido.
A maneira mais fácil de declarar a sobrecarga e obter o comportamento esperado é usar std::vector<G>
diretamente como o tipo de argumento:
template<typename G>
std::ostream& operator<<(std::ostream& os, const std::vector<G>& chromosome)