How to explicity call a templated overload of operator <<? - c++

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.

Related

const struct object access member functions (and an operator overloading question)

I tried to overload the << operator for my custom struct, but encountered error C2662, code as follows:
struct HP{
int max_hp;
int hp;
HP(int max_hp){
this->max_hp=max_hp;
this->hp=max_hp;
}
// this const declarative doesn't support const HP& obj argument
const string repr(){
stringstream hp_stats;
hp_stats << this->hp << "/" << this->max_hp;
return hp_stats.str();
}
// This is fine with const HP& obj argument
const string repr(){
stringstream hp_stats;
hp_stats << this->hp << "/" << this->max_hp;
return hp_stats.str();
}
};
// would cause error C2662
ostream& operator<<(ostream& out, const HP& obj){
out<<obj.repr();
return out;
}
I've found that this is because the implicitly access of this pointer, and const attempts to convert *this into const *this and thus fails. But as I changed from const string repr() to string repr() const, it magically worked and compiled successfully.
What's the difference between a const T func(){} and T func() const, which makes a const struct object invoking feasible?
I read it on cppreference.com, but still unsure why << overloading has to be declared outside a class scope.
Suppose I have repr() member function for struct HP, struct Shield, etc. I tried to make the << overloading a template function, but it turns that I am overloading << at global scope. Can I specify the T I am about to apply, OR apply the overloading across several classes with same member function, WITHOUT classes defined under one base class?
// complier thinks such overloading is global on <<
template <typename T>
ostream& operator<<(ostream& out, const T& obj){
out<<obj.repr();
return out;
}
const T func() {} means that the return type is const T and the function might mutate the object. Whereas, T func() const {} means that the return type is non-const but the object is unaltered (const). You can also have both or neither const.
It doesn't have to be declared outside the class, it can be declared inside as a friend (non-member) function. That works here as well. However, for a member function: since operator<<()'s first parameter is ostream&, you could only declare it in ostream and not HP, which won't work. Remember, member operators are like *this as the first argument of non-member operators.
Yes, you can do this. The easiest is to delegate to a common print function; the more elegant way is to use std::enable_if, e.g.:
template <typename T>
std::enable_if_v<std::is_same_v<T, HP> || std::is_same_v<t, Shield>, ostream&>
operator<<(ostream& out, const T& obj)
{
out<<obj.repr();
return out;
}
You can also write the conditions as a template and then re-use it:
template<typename T>
static constexpr bool is_printable_v = std::is_same_v<T, HP> || std::is_same_v<T, Shield>;
template <typename T>
std::enable_if_v<is_printable_v<T>>, ostream&>
operator<<(ostream& out, const T& obj) { ... }

Templated ostream operator << for multiple types within a namespace

I have a namespace with several structs and enum classes inside of it. For each type, I have a toString() method. Here is a small example:
namespace test {
struct A {
int i;
};
struct B {
float j;
};
std::string toString(const A &a){
return to_string(a.i);
}
std::string toString(const B &b){
return to_string(b.j);
}
}
I want to provide a templated operator<< which captures only these types, but not for types outside of this namespace:
template<class T>
std::ostream & operator<<(std::ostream &out, const T &t){
out << toString(t);
return out;
}
However, this gives me the following compilation error:
error: ambiguous overload for 'operator<<' (operand types are 'std::stringstream {aka std::__cxx11:basic_stringstream<char>}' and 'const char*')
How can I write a templated operator overload for this?
I solved it using concept & requires of C++20 (gcc >= 10.1):
template <typename T>
concept HaveToString = requires (T t) {
{ toString(t) };
};
template<HaveToString T>
std::ostream & operator<<(std::ostream &out, const T& t){
out << toString(t);
return out;
}
int main() {
test::A a;
std::cout << a << std::endl;
return EXIT_SUCCESS;
}
EDIT
For C++11:
template<typename T, typename = decltype(toString(std::declval<T>()))>
std::ostream & operator<<(std::ostream &out, const T& t){
out << toString(t);
return out;
}
Or as #MooingDuck mentioned in the comments:
template<typename T>
auto operator<<(std::ostream &out, const T& t) -> decltype(out<<toString(t)) {
out << toString(t);
return out;
}
Explanations
First of all, a really good article about unevaluated operands. It will help to understand what is going on in the expressions: decltype(toString(std::declval<T>())) and decltype(out<<toString(t)) which are both basically doing the same thing-> Setting a rule that any call to this function, have to support the call to toString function with the T parameter type.
First Approach
decltype(toString(std::declval<T>()))
Let's split this complex expression into sub expressions, from the inside out:
decltype(toString( std::declval<T>() ))
std::declval<T>() In some very simple words - means that we are "assuming" we created a variable of the type T at a compile time (If you didn't read the article yet, now it's a really good time to do so). The important thing to know before continue- we didn't do it, the important word is assuming.
decltype( toString(std::declval<T>()) )
The magic continue all the way up to decltype which checking for the type of the unevaluated expression within it. So, if toString that calls T type variable exists, it will return the value that toString function returns. If this function doesn't exist, a compile time error will be thrown (or in this context, the compiler won't deduce this function for the given type).
typename = decltype(toString(std::declval<T>()))
This section in the template meant to enable this function whenever the type returning from decltype is legal.
#MooingDuck Approach
auto operator<<(std::ostream &out, const T& t) -> decltype(out<<toString(t)) { /*...*/ }
Return value: auto
C++11: Deduced by the expression the after the operator ->.
After C++14: Calculated at compile time by the return expression inside the function (if there is no return expression, the return value deduced at compile time to void).
-> decltype(out<<toString(t))
Define the return value type.
decltype(out<<toString(t))
As explained before, whatever comes inside decltype is unevaluated expression. The compiler won't evaluate this expression, but it will make sure that the expression can be evaluated at runtime (or else an exception will be thrown, or in this case, the compiler won't deduce this function), and it will return the type of the returned value from this expression.

Proper syntax for specifying template parameters for a global templated operator?

Sorry for the trivial question, but I could not Google this with a couple of attempts and decided to ask here. What is the proper way to specify the template parameters in this case:
#include <sstream>
struct bar
{
int foo = 5;
};
template<size_t i>
std::ostream& operator<<(std::ostream& s, bar b)
{
s << b.foo + i;
return s;
}
int main(int argc, char** argv)
{
std::stringstream s;
bar b;
// Proper way of writing s << bar ?
return 0;
}
You are forced to use the long form to call the operator, and explicitly pass template parameters
int main()
{
std::stringstream s;
bar b;
::operator<<<1>(s, b);
return 0;
}
Demo
The ::operator<<<1>(s, b); syntax looks weird, but focus on the ::operator<< portion, which is referencing the operator in the global namespace. Since you've written it as a template, and the template parameter (size_t) cannot be inferred from the arguments, you are forced to use an explicit template parameter in the form of <1> (<2>, <3>, etc.)
Since your function template has no deducable argument, you will have to explicitly call it with the template argument. Its ugly.
operator << <4>(s, b);
Another ugly hack is to use a proxy template-class:
template<std::size_t K>
struct bT{
bT(bar& bb) : b(bb) {} bar& b;
static constexpr std::size_t i = K;
};
Then modify your template operator << to:
template<size_t i>
std::ostream& operator<<(std::ostream& s, bT<i> b)
{
s << b.b.foo + b.i;
return s;
}
And call like:
s << bT<4>(b);
Demo.. Anyhow we panel beat this, it still ends up being ugly. Save everyone the stress, and use a named function. That will be more legible an intuitive.

Specialising a templated function for a templated (Armadillo) class

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...

What does this "<>" mean inside a template class function?

There is a code like this.
const std::string DeviceTypeStrings[] ={ "A", "B", "C", "D", "E" };
enum DeviceTypes { A = 0, B, C, D, E };
template <DeviceTypes T> class DeviceType;
template <DeviceTypes T> std::ostream& operator<< (std::ostream& output, const DeviceType<T>& dev);
template <DeviceTypes T> class DeviceType {
public:
static const int value = T;
static const std::string string;
friend std::ostream & operator<< <>(std::ostream & output, const DeviceType<T> & deviceType );
};
template <DeviceTypes T> const std::string DeviceType<T>::string = DeviceTypeStrings[T];
template <DeviceTypes T> std::ostream & operator<< (std::ostream & output, const DeviceType<T> & deviceType ){
if ( DeviceType<T>::string.find(' ') != std::string::npos ){
return output << "\"" << DeviceType<T>::string << "\"";
} else {
return output << DeviceType<T>::string;
}
}
int main () {
DeviceType<A> myType;
std::cout << myType << std::endl;
return 0;
}
Note there is a "<>" after the operator<< inside class DeviceType, what does "<>" mean? If you could, why does it has to be there?
It simply means that the friend declaration refers to a particular specialization of function template operator << (declared previously), not to some yet undeclared ordinary non-template function operator <<.
Which specialization this friend declaration refers to is determined by the argument deduction mechanism, i.e. the actual template arguments are implicitly derived from the parameter types used in friend declaration. For this reason there's no need to specify template arguments in <> explicitly, but an empty pair of <> is still required.
In other words, the author of the code could've stated explicitly
friend std::ostream & operator<< <T>(std::ostream & output,
const DeviceType<T> & deviceType );
(note the explicit T in <T>). However, since the compiler can figure it out by itself (derive it from the type of the second argument), it is perefectly possible to put just an empty pair of <> there.
Now, if the code just said
friend std::ostream & operator<<(std::ostream & output,
const DeviceType<T> & deviceType );
(i.e. no <> at all), it would befriend an ordinary (non-template) function operator <<, which is not what the author wanted.
Overload resolution feature that works in this friend declaration can be illustrated without any friend declarations by the following simple example
void foo(int);
template <typename T> void foo(T);
int main() {
foo(42); // calls non-template function
foo<int>(42); // calls template function, explicit template argument given
foo<>(42); // calls template function, template argument deduced by compiler
}
When you want to tell the compiler that you specifically want to refer to a template version of the function, you have to include triangular brackets into the reference, even if there's nothing between them.
The compiler checks for < after operator<< if it's a friend function of a template class. It's considered as the template argument list.
It is same like at this link.
See the "Template specialization" chapter.
// template specialization
#include <iostream>
using namespace std;
// class template:
template <class T>
class mycontainer {
T element;
public:
mycontainer (T arg) {element=arg;}
T increase () {return ++element;}
};
// class template specialization:
template <>
class mycontainer <char> {
char element;
public:
mycontainer (char arg) {element=arg;}
char uppercase ()
{
if ((element>='a')&&(element<='z'))
element+='A'-'a';
return element;
}
};
int main () {
mycontainer<int> myint (7);
mycontainer<char> mychar ('j');
cout << myint.increase() << endl;
cout << mychar.uppercase() << endl;
return 0;
}
It's just how you refer to already defined template object (or function).