I'm trying to get up to speed on "modern" C++ particularly using templates. I've got a class Point that overrides the << operator. Works fine. So then I decided to add nice print out for containers in general.
My question - is there a way write a template for multiple container types similar to the code below?
template <typename T>
std::ostream& operator <<(ostream& os, const vector<T>& v)
{
os << "\n";
for( auto x : v ) { os << "\n\t" << x; }
os << "\n";
return os;
}
The code above correctly outputs vectors in a multi-line format as long as T has an << operator. I'd like to make this work for other containers like lists etc.
I also realize that it's probably a bad idea (or at least rude) to override output for containers in a general way for all types. So ultimately the template code above would have the typename hard coded/restricted to 'Point' and a template-ed container.
OK, implimenting AndyG's suggestion I have the following full code:
#include <iostream>
#include <map>
#include <vector>
using namespace std;
struct Point {
double x, y, z;
Point() : x(0), y(0), z(0) {};
Point(double a, double b, double c) : x(a), y(b), z(c) {}
Point(double a[]) : x(a[0]), y(a[0]), z(a[0]) {}
friend std::ostream& operator <<(ostream& os, const Point&p)
{
os << p.x << ", " << p.y << ", "<< p.z;
return os;
}
};
template <template<class> class C, class... T>
std::ostream& operator <<(ostream& os, const C<T...>& v)
{
os << "\n";
for( auto x : v ) { os << "\n\t" << x; }
os << "\n";
return os;
}
vector<Point> vp = { { 1, 2, 3 },
{ 5, 7, 4 },
{ 8, 2, 5 }
};
int main(int argc, char* argv[])
{
cout << vp[0]; // works
cout << vp; // dosen't work,
}
But still no luck. The compiler can't match operator<< with 'std::vector'
I had tried many variations of the template <template> prior to my first post with about the same results. I can get a template that compiles but the compiler fails to match the operator function and use the template.
I wasn't aware of the variadic template pack, obviously a good idea but doesn't solve the problem.
OK, AndyG set me on the right track and I think I understand what is going on.
The following code doesn't work because the std::container templates also have an allocator parameter which has a default and you rarely need to use it. Hence we think of most container templates of taking just the type/class that they will contain.
This
template < template <class> class C, class... T>
std::ostream& operator <<(ostream& os, const C<T...>& v)
{ ... }
doesn't work because the template we are going to call C in our operator<< function takes two parameters not one. Therefore the compiler won't find a match.
This will work:
template < template <class, class> class C, class... T>
std::ostream& operator <<(ostream& os, const C<T...>& v)
because the compiler can match vector<Point, Alloc> to template<class, class> which we choose to call C. It can then use our function template to generate an overload for operator <<.
Note that there are a number of things wrong with this approach in general. The template <class, class> isn't going to match a std::map which takes 4 parameters, 2 of which are defaulted. Even worse it will match any two argument template that may or may not be a container.
We can fix this by using a varg parameters: template <class, class...> however we are still screwed because now the << function template is dealing with std::pairs from the iterator which the compiler doesn't know how to cout <<.
So while this was a useful exercise for me to understand templates better, don't do it.
This link has a neat container pretty print lib: Pretty-print C++ STL containers
Related
Assume that I want to implement operator<< for all types. I would do:
template <typename T>
std::ostream& operator<<(std::ostream& out, T&& t) {
return out << "DEFAULT";
}
But this won't work because of ambiguity (in cases where the already specified operator<< is a free function). So I tried to restrict this with a concept:
template <typename T>
concept printable = requires(const T& t, std::ostream& out) {
out << t;
};
This correctly reports that ints, std::strings and whatnot are printable, but std::vectors or some_user_defined_structs (without overloaded <<) are not.
What I wanted was to use this concept with my (overly generic) operator<<:
#include <iostream>
#include <vector>
template <typename T>
concept printable = requires(const T& t, std::ostream& out) {
out << t;
};
template <typename T>
requires (!printable<T>)
std::ostream& operator<<(std::ostream& out, T&& t) {
return out << "DEFAULT";
}
int main() {
std::cout << std::vector<int>();
}
But this lead to:
In instantiation of 'std::ostream& operator<<(std::ostream&, T&&) [with T = const char (&)[8]]':
recursively required from 'std::ostream& operator<<(std::ostream&, T&&) [with T = const char (&)[8]]'
required from 'std::ostream& operator<<(std::ostream&, T&&) [with T = const char (&)[8]]'
required from here
fatal error: template instantiation depth exceeds maximum of 900 (use '-ftemplate-depth=' to increase the maximum)
6 | out << t;
| ~~~~^~~~
compilation terminated.
It seems that there is an instantiation loop. In order to check whether we should use my <<, printable is being checked and by doing so, << is attempted to be generated, which leads to a loop.
Is there any mechanism that would prevent such loop? Can we constrain types in such a way that the template will be generated only if it needs to? As for the use-cases, assume that, for some reason, I never want the compilation to fail when someone tries to << something to std::cout.
You can't provide an operation if and only if the operation isn't provided. That's inherently self-recursive.
What you can do instead is add another layer of indirection. Like so:
template <typename T>
void print(std::ostream& os, T&& t) {
if constexpr (printable<T>) {
os << t;
} else {
os << "DEFAULT";
}
}
I was messing around with this and was able to come up with something similar to what the question describes, the only difference being that you have to opt in to using it via a using namespace directive. (godbolt demo)
#include <iostream>
#include <vector>
#include <utility>
template <typename T>
// T can be a reference type
concept printable = requires(std::ostream& out, T t) {
out << std::forward<T>(t);
};
template <typename T>
requires (!printable<T>)
std::ostream& default_print(std::ostream& out, T&& t) {
return out << "DEFAULT";
}
namespace default_ostream
{
template<typename T>
std::ostream& operator<<(std::ostream& out, T&& t)
requires requires { default_print(out, std::forward<T>(t)); }
{
return default_print(out, std::forward<T>(t));
}
} // namespace default_ostream
int main()
{
using namespace default_ostream;
std::cout << std::vector{ 0, 1, 2 } << '\n';
std::cout << "Hello!\n";
std::cout << 2.234 << '\n';
}
This program will output the following with all of GCC, Clang and MSVC:
DEFAULT
Hello!
2.234
Putting the default operator<< in a seperate namespace and deferring the !printable<T> requirement to print_generic seems to work. With this you will have to do using namespace default_ostream; if you want this behaviour, which cannot appear at global scope, but is fine at function (or some namespace) scope.
This works because printable doesn't see the generic operator<< when it's used as a requirement to default_print, that way it can't get selected for out << std::forward<T>(t) in the requirement and avoids recursive instantiation.
When you want to use the generic operator<<, you have to bring it into the local scope with using namespace default_ostream;, so it participates in overload resolution and because of its requirement it will only get selected if there's no other operator<< available.
Consider the following example code for overloading the operator<< for a class A:
#include <iostream>
class A {
template <typename T>
friend A &operator<<(A &a, const T &t)
{
std::cout << t << std::endl;
return a;
}
friend A &operator<<(A &a, const std::string &t)
{
return operator<<<std::string>(a, t + "x");
}
};
My intention is that the second operator explicitly calls the first one.
However, in g++ 7.4 this fails with
In function 'A& operator<<(A&, const string&)':
error: 'operator<<' not defined
return operator<<<std::string>(a, t + "x");
^~
error: expected primary-expression before '>' token
return operator<<<std::string>(a, t + "x");
^
I however do not see why this should not compile.
Here is the code in godbolt.
In-class friend functions are not exposed to any scope. Friend injection used to be a thing (before ADL was invented), but now there is no way to call them except with ADL unless you declare them beforehand. In this case, a workaround is to declare the template function outside the class beforehand.
class A;
template <typename T>
A &operator<<(A &a, const T &t);
Call a function instead of calling an operator.
#include <iostream>
class A {
template <typename T>
static A &print(A &a, const T &t)
{
std::cout << t << std::endl;
return a;
}
template <typename T>
friend A &operator<<(A &a, const T &t)
{
return print(a, t);
}
friend A &operator<<(A &a, const std::string &t)
{
return print(a, t + "x");
}
};
You seem to wish to specialize your template function, but you're not doing it quite right. It should look more like this:
template <> friend A& operator<< <std::string>(A &a, const std::string &t)
{
// Print in here some how. It's not exactly clear to me how you intend to
// do this, as doing something like a << t will create infinite recursion
// finally, return a
return a;
}
Your other option is to switch the order of the functions, creating the template function after you create your first function:
friend A &operator<<(A &a, const std::string &t)
{
// Again, still not sure what you want to do here
// I just want to stress again though, don't do something
// like a << t, or operator<<(a,t)
// That will crash hard and fast, as there is no way to resolve
// it. It will create infinite recursion
return a;
}
template <typename T>
friend A &operator<<(A &a, const T &t)
{
std::cout << t << std::endl;
return a;
}
My intention is that the second operator explicitly calls the first one.
So, first, you will need to actually need the first option in that case.
Second, to do this, you will need to choose a type for t. You would do it like this:
operator<< <SomeType>(a,t);
Keep in mind, t will need to be implicitly converted to SomeType. Else, SomeType needs to be created by calling its constructor:
operator<< <SomeType>(a,SomeType(/* parameters to construct a SomeType ... */));
Note: Doing something like operator<< <SomeType>(a,t + "x") will always become infinitely recursive, and ultimately crash. This is because t + "x" is always an std::string. That means the compiler will always call this overload of the function infinitely, until it finally crashes from a stack overflow. So don't do that.
Since std::to_string is added to c++11, I started implementing to_string instead of the more traditional operator<<(ostream&, T). I need to link the two together in order to incorporate libraries that rely on operator<<(ostream&, T). I want to be able to express that if T has operator<<(ostream&, T), use it; otherwise, use std::to_string. I am prototyping a more limited version that enables operator<< for all enum classes.
enum class MyEnum { A, B, C };
// plain version works
//std::ostream& operator<<(std::ostream& out, const MyEnum& t) {
// return (out << to_string(t));
//}
// templated version not working
template<typename T,
typename std::enable_if<std::is_enum<T>::value>::type
>
std::ostream& operator<<(std::ostream& out, const T& t) {
return (out << to_string(t));
}
Compiler says error: no match for 'operator<<' (operand types are 'std::ostream {aka std::basic_ostream<char>}' and 'MyEnum')
cout << v << endl;
Questions:
Why is the template function not found by the compiler?
Is there a way to implement the general solution?
The following will work if there exists a std::to_string that accepts an argument of type const MyEnum (which doesn't exist according to clang-703.0.31).
#include <iostream>
#include <type_traits>
enum class MyEnum { A, B, C };
template<typename T>
typename std::enable_if<std::is_enum<T>::value, std::ostream &>::type
operator<<(std::ostream& out, const T& t) {
return (out << std::to_string(t));
}
int main() {
MyEnum a;
std::cout << a << std::endl;
}
According to the docs, there are two ways of using std::enable_if, one of which is to make the return type of a function only valid if T is an enum type (in your case). This is what this code shows. If std::is_enum<T>::value is true, then std::enable_if<std::is_enum<T>::value, std::ostream &>::type results in std::ostream & and is not defined (which makes the compiler yell at you) otherwise.
You could probably write something like my::to_string which would try to convert to a string user-defined types as well:
namespace my {
template<typename T>
typename std::enable_if<! std::is_void<T>{} && std::is_fundamental<T>{}, std::string>::type
to_string(T t) {
return std::to_string(t);
}
std::string to_string(std::nullptr_t) = delete;
std::string to_string(MyEnum a) {
return "This is an enum";
}
}
Then, you can use my::to_string instead of std::to_string in your operator<<:
return (out << my::to_string(t));
EDIT: using my::to_string now results in a compilation error when it's argument is void or std::nullptr_t.
Why your code didn't work
Look at the example in the docs:
// 2. the second template argument is only valid if T is an integral type:
template < class T,
class = typename std::enable_if<std::is_integral<T>::value>::type>
bool is_even (T i) {return !bool(i%2);}
As you can see, the second template argument is written with class = /* enable_if stuff */, but in your code you do simply template< typename T, /* enable_if stuff */ >. So, if you follow the docs, you'll get the correct result - the compiler will say it couldn't find a specialization of std::to_string that accepted an enum as a parameter.
I'm trying to put together a very simple logging class which treats certain types, and particularly vectors, specially. I want to have a default behaviour when using the << operator, but modify it in certain cases. The set-up is:
class LoggerStream
{
template <typename ArgType>
LoggerStream & operator<< (const ArgType &arg)
{
// Standard logging logic
return *this;
}
template <typename DataType>
LoggerStream & operator<< (const std::vector<DataType> &arg)
{
// Specialised logging logic
return *this;
}
template <typename DataType>
LoggerStream & operator<< (const arma::Col<DataType> &arg)
{
// Specialised logging logic
return *this;
}
LoggerStream & operator<< (const double &arg)
{
// Specialised logging logic
return *this;
}
// Other stuff...
};
The double case works fine. The problem is that for subclasses of the vector types, the general-purpose template seems to take priority, and the more specific template gets ignored.
Since all three templated cases have just one generic template parameter I guess the the vector cases aren't considered the most specialised, but if it were considered ambiguous I would expect a compiler error. (It compiles just fine.) So how can I indicate a specialisation but still generalise over the type of the vector elements? Thanks in advance.
I guess this is to do with some detail of how the Col class is implemented. I'm using (and aliasing) arma::Col<T>::fixed<N> as well, but writing a specific overload for that doesn't seem to help. Any ideas welcome.
I cannot reproduce it. This code works as it is supposed to:
#include <iostream>
#include <vector>
using namespace std;
class LoggerStream
{
public:
template <typename ArgType>
LoggerStream &operator<< (const ArgType &arg)
{
// Standard logging logic
cout << "In general" << endl;
return *this;
}
template <typename DataType>
LoggerStream &operator<< (const std::vector<DataType> &arg)
{
// Specialised logging logic
cout << "In vector" << endl;
return *this;
}
};
int main()
{
LoggerStream foo;
vector<int> v;
foo << v; // calling the vector specialization
}
I'm answering my own question not because I have a resolution (yet), but to clarify the problem after further investigation. In fact this doesn't seem to be specific to arma::Col; rather, the issue seems to be in the precedence given to the more specific overload when the argument is of a subclass type. A simple example that shows the problem without using Armadillo is
#include <iostream>
#include <vector>
using namespace std;
template <typename DataType>
class MyVector : public std::vector<DataType> {};
class LoggerStream
{
public:
template <typename ArgType>
LoggerStream &operator<< (const ArgType &arg)
{
cout << "In general" << endl;
return *this;
}
template <typename DataType>
LoggerStream &operator<< (const std::vector<DataType> &arg)
{
cout << "In vector" << endl;
return *this;
}
};
int main()
{
LoggerStream foo;
std::vector<float> v;
foo << v; // Prints "In vector", as expected
MyVector<float> w;
foo << w; // Prints "In general" (but only if the general case exists)
}
So, using the subclass MyVector produces a call to the general function, not the specialised one. But, if the first version is commented out then both calls produce "In vector". So for some reason both are deemed suitable, but the general function is preferred.
What I still don't know is how to stop this from happening without explicitly writing out a load of specific overloads for individual subclass cases...
This question already has answers here:
std::endl is of unknown type when overloading operator<<
(6 answers)
Closed 10 years ago.
I'm currently writing a wrapper for an std::stringstream and i want to forward all the operator<< calls through my class to the std::stringstream. This works just fine now
(thanks to this question: wrapper class for STL stream: forward operator<< calls), but there is still one issue with it.
Let's say I have the following code:
class StreamWrapper {
private:
std::stringstream buffer;
public:
template<typename T>
void write(T &t);
template<typename T>
friend StreamWrapper& operator<<(StreamWrapper& o, T const& t);
// other stuff ...
};
template<typename T>
StreamWrapper& operator<<(StreamWrapper& o, T const& t) {
o.write(t);
return o;
}
template<typename T>
void StreamWrapper::write(T& t) {
// other stuff ...
buffer << t;
// other stuff ...
}
If I now do this:
StreamWrapper wrapper;
wrapper << "text" << 15 << "stuff";
This works just fine. But if I want to use the stream modifiers like std::endl, which is a function according to http://www.cplusplus.com/reference/ios/endl, I simply doesn't compile.
StreamWrapper wrapper;
wrapper << "text" << 15 << "stuff" << std::endl;
Why? How can I forward the stream modifiers too?
See this answer.
You'll want
typedef std::ostream& (*STRFUNC)(std::ostream&);
StreamWrapper& operator<<(STRFUNC func) // as a member, othewise you need the additional StreamWrappe& argument first
{
this->write(func);
return *this;
}
You seem to be doing a bit of extra work. I normally use:
class StreamWrapper
{
// ...
public:
template <typename T>
StreamWrapper& operator<<( T const& obj )
{
// ...
}
};
With a modern compiler, this should work for all concrete
types. The problem is that manipulators are template functions,
so the compiler is unable to do template argument type
deduction. The solution is to provide non-template overloads
for the types of the manipulators:
StreamWrapper& operator<<( std::ostream& (*pf)( std::ostream& ) )
{
// For manipulators...
}
StreamWrapper& operator<<( std::basic_ios<char>& (*pf)( std::basic_ios<char>& )
{
// For manipulators...
}
Type deduction will fail for the manipulators, but the compiler
will pick up these for function overload resolution.
(Note that you might need even more, for things like std::setw(int).)