Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typedef and ostream operator for a std::vector

I created a class Chromosome that ended up simply being a wrapper for vector with an ostream operator, so I've decided to typedef vector instead. However, I'm having trouble with the templated ostream operator... Is this the best way to go about it? (I've seen a few approaches and have failed to get any to work)

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;
}

At the moment the error I'm getting is:

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

Cheers.

like image 795
Rhys van der Waerden Avatar asked Feb 25 '23 03:02

Rhys van der Waerden


2 Answers

Unfortunately, there's no clean way to do this because the compiler can't deduce the type of G from the function declaration

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

The reason is that if you were to specialize Chromosome for different types, you could end up in a situation where the compiler couldn't unambiguously infer G. For example:

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;
};

Now, what would happen if you did this?

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

The compiler can't tell if G is double or int in this case, because both Chromosome<int> and Chromosome<double> have vector<double> as their nested type.

To fix this, you'll have to explicitly use the type vector<G> as the argument:

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

Unfortunately, there really isn't a better way of doing this. It's not really a defect in the language, since there's a good reason to prohibit it, but it does actually prevent you from doing what you want to in this context.

like image 81
templatetypedef Avatar answered Mar 07 '23 15:03

templatetypedef


The member typedef type is a dependent name: its meaning is dependent upon the template parameter G. You need to use a typename to tell the compiler that type names a type:

const typename Chromosome<G>::type&

For the full explanation, consider reading the Stack Overflow C++ FAQ article, Where to put the “template” and “typename” on dependent names.

As @templatetypedef alludes to in the comments, while this will enable the code to compile, it won't "work" to allow you to insert an std::vector<G> into an std::ostream because type is in a nondeduced context.

The easiest way to declare the overload and get the expected behavior is to use std::vector<G> directly as the argument type:

template<typename G>
std::ostream& operator<<(std::ostream& os, const std::vector<G>& chromosome)
like image 29
James McNellis Avatar answered Mar 07 '23 14:03

James McNellis