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

Time:08-09

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.

  1. What's the difference between a const T func(){} and T func() const, which makes a const struct object invoking feasible?

  2. I read it on cppreference.com, but still unsure why << overloading has to be declared outside a class scope.

  3. 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;
}

CodePudding user response:

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

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

  3. 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) { ... }
  • Related