Reputation: 68738
Suppose I have a function that has a parameter that is overloaded by many different types. For example:
ostream& operator<<(ostream&, ...);
so if I have a class Foo
class Foo { ... }
I can define an overload
ostream& operator<<(ostream&, const Foo&);
and it works fine.
Now let's suppose I have a another class:
template<class T>
class vector {...}
And now I want to define an overload of the function that takes any vector<T>
where T can be any valid type. Is this possible to do without defining it for all possible input parameters? What signature should I use for such a definition?
template<class T>
ostream& operator<<(ostream&, const vector<T>& v);
?
Background:
In this instance I actually do want to write an overload for std::vector:
ostream& operator<<(ostream&, const std::vector<T>& x);
to write out something like "{2, 4, 8}" or similiar, as my logging system is built on top of ostream and uses operator<< internally to "stringify" types.
I thought I would pose the question in general, but I should add the constraint that I cannot alter the class (std::vector for example) in this case.
Upvotes: 2
Views: 155
Reputation: 35469
The usual solutions both rely on ADL. The first one is writing a function template, which looks just like in your question:
namespace ns {
// vector must reside in this namespace
template<typename T>
std::ostream& operator<<(std::ostream& os, vector<T> const& v);
// define somewhere
}
The other way doesn't require writing a template, but is intrusive because it's in the class definition:
namespace ns {
template<typename T>
class vector {
/* stuff */
public:
/* we're using the injected name for the class,
but vector<T> works just as well */
friend
std::ostream& operator<<(std::ostream& os, vector const&)
{
/* caveat: must be defined inline
there's no other way to write the definitions for all T */
}
};
}
In both cases, client code looks like this:
std::ostream& os = /* a stream from somewhere */;
ns::vector<foo> v;
os << v; // adl picks up correct overload
You should probably use the first option, the second one is normally picked when implementing an operator for a class (i.e. you're writing vector
in the first place). Note that due to the nature of namespaces you can reopen namespace ns
to put your operator in here even if you're not the writer of ns::vector
... except if it's the case that this is about namespace std
, since only writing template specializations is allowed in some cases (and to clarify, function templates can't be partially specialized, and we can't use a total specialization here). Given that there is such a thing as std::vector
, it may be the case that you're interested in the next last ditch option.
What if you want to add an overload or write a function template accepting a template from namespace std
?
This is strictly my opinion, but I think there's only one sane way to do that: put your functionality in the namespace of your choice (which is obviously not namespace std
since, again, it's not allowed to put it there), and have client-code ask for that.
namespace stream_extensions {
template<typename T>
std::ostream& operator<<(std::ostream& os, std::vector<T> const& v);
// define somewhere
}
Client code looks like:
std::ostream& os = /* stream from somewhere */;
std::vector<T> v;
// os << v; won't work, can't find relevant operator
using namespace stream_extensions;
os << v; // Okay, stream_extensions::operator<< is in scope
If the name of the namespace is well-chosen and restricted to only contain a limited number of things in it, I think such a using directive is a very reasonable choice. Consider a using namespace literals::chrono;
which allows one to write e.g. cond.wait(24_ms);
instead of cond.wait(std::chrono::milliseconds(24));
.
The big caveat of this technique is that the functionality must be entirely new. This isn't a way to emulate partial specialization for function template: if there is a generic function template in namespace std
then there is a very high risk that it will be either preferred through ADL or that you'll end up with an overload resolution ambiguity. The example here makes sense because there is no unrestricted template<typename T> std::ostream& operator<<(std::ostream& os, T const& t);
(or a similar member version).
Upvotes: 4
Reputation: 40643
The signature that you gave is the correct one to use:
template<class T>
ostream& operator<<(ostream&, const vector<T>& v);
This is not a partial template specialization, but rather a base template function which will be matched by the usual function overload resolution rules.
Upvotes: 1