Typedef and ostream operator for a std::vector
Asked Answered
S

2

4

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.

Slothful answered 12/2, 2011 at 1:33 Comment(0)
M
7

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.

Msg answered 12/2, 2011 at 1:47 Comment(1)
Thanks, I understand how this works now. using vector is completely fine, as I was only trying to use Chromosome<G>::type for readability. Could you explain "no typename needed here" though?Slothful
F
1

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)
Fransen answered 12/2, 2011 at 1:36 Comment(3)
Actually, this won't work because the compiler can't deduce G in this context. This is partly because you can produce some situations with template specialization where the compiler couldn't know which type to use.Msg
@templatetypedef: Good point; the dependent name isn't the only problem here.Fransen
Thanks for the answer, I found templatetypedef's slightly clearer so he gets this one.Slothful

© 2022 - 2024 — McMap. All rights reserved.