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(){}
andT func() const
, which makes a conststruct
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 forstruct 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 theT
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;
}
CodePudding user response:
const T func() {}
means that the return type isconst 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 neitherconst
.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: sinceoperator<<()
's first parameter isostream&
, you could only declare it inostream
and notHP
, 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>
using 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) { ... }