0

I currently have the following code that works:

class Element{
    public:
        int i;
        int j;
        int x;
};

class SparseMatrix{
    private:
        int m; 
        int n;
        int numElts;
        Element *ele;

    public:
        SparseMatrix(int m, int n, int numElts){
            this -> m = m;
            this -> n = n;
            this -> numElts = numElts;

            ele = new Element[this -> numElts];
        }
        
        ~SparseMatrix(){
            //destructor logic
        }

        SparseMatrix operator+(SparseMatrix &s);

        friend std::istream & operator>>(std::istream &is, SparseMatrix &s);
        friend std::ostream & operator<<(std::ostream &os, SparseMatrix &s);
};

SparseMatrix SparseMatrix::operator+(SparseMatrix &s){
    //addition logic
}

std::istream & operator>>(std::istream &is, SparseMatrix &s){
    //cin logic
}

std::ostream & operator<<(std::ostream &os, SparseMatrix &s){
    //cout logic

Notice that the member variable x in Element is hard-coded as an integer value.

Instead, I'd like to use the following templating:

template <typename T>
class Element{
    public:
        int i;
        int j;
        T x;
};

; the problem is that every time I try to percolate that change through the rest of my code, I'm left with half a million errors.

Note: I've tried so many different things from Stack Overflow that I'm not going to mention them here; believe me when I say: It's been a lot!

I'm a relative newbie, so can someone please tell me what templating measures I need to employ to arrive at error-free inheritance here? I would like SparseMatrix to accept and utilize the templated Element class, and I'd like the functions defined throughout to also work with the templated version of Element + SparseArray.

Update 1: This is in response to @user17732522's suggestion to do the seemingly straightforward templating.

Here's what I did:

template <typename T>
class Element{
    public:
        int i;
        int j;
        T x;
};

template <typename T>
class SparseMatrix{
    private:
        int m; 
        int n;
        int numElts;
        Element<T> *ele;

    public:
        SparseMatrix(int m, int n, int numElts){
            this -> m = m;
            this -> n = n;
            this -> numElts = numElts;

            ele = new Element<T>[this -> numElts];
        }

        ~SparseMatrix(){
            delete [] ele;
        }

        SparseMatrix operator+(SparseMatrix &s);

        friend std::istream & operator>>(std::istream &is, SparseMatrix &s);
        friend std::ostream & operator<<(std::ostream &os, SparseMatrix &s);
};

template <typename T>
SparseMatrix<T> SparseMatrix<T>::operator+(SparseMatrix<T> &s){
    //sum logic
}

template <typename T>
std::istream & operator>>(std::istream &is, SparseMatrix<T> &s){
    //cin logic
}

template <typename T>
std::ostream & operator<<(std::ostream &os, SparseMatrix<T> &s){
    //cout logic
}

What I get out from VSCode is 7 errors saying that more than one operator >> matches these operands, -Wnon-template-friend warnings, etc.

Did I miss something in my implementation?

cstover
  • 171
  • 1
  • 9
  • Totally unrelated: Your `operator+` is likely going to lead to problems. The method prototype looks more like you're trying to implement `+=`. [Here's some reading to help you out](https://stackoverflow.com/q/4421706/4581301). – user4581301 May 24 '23 at 21:43
  • 2
    `SparseMatrix` also needs to be templated on `T` to make this work and then it should be straight-forward. Your class violates the rule-of-zero/three/five btw. This should not happen. It will cause undefined behavior when the class is copied. This can be avoided by making `ele` a `std::vector` instead of a raw pointer and then dropping the destructor declaration completely in order to follow the rule-of-zero. – user17732522 May 24 '23 at 21:46
  • 2
    Recommendation: `Element *ele;` -> `std::vector ele`. Even if you're not allowed to hand the program in using `vector` start by using `vector`. Debug the much, MUCH easier program, and then when you know it works, proceed to shooting yourself in the face with `new`. If you have to debug all of the problems you're going to have with the general business logic of `SparseMatrix` at the same time as you have to debug all of the memory management woes `new` brings, you'll be at this far, far longer than it'll take to debug the two separately. – user4581301 May 24 '23 at 21:50
  • 1
    Side note replace manual memory management like `ele = new Element[this -> numElts];` with std::vector and learn how to use initializer lists in constructors and learn about const references. If you only have public members and no operations use `struct` instead of `class` (like for Element). So all in all get some of the C++ basics right before you start working on templates. – Pepijn Kramer May 24 '23 at 21:50
  • @user4581301 - Thank you for the reference! I'll check that out ASAP! – cstover May 24 '23 at 21:55
  • @user17732522 - When I implement the (seemingly-straightforward) templating you mention, I get a bunch of errors in VSCode. I'll post an answer down below to show what I did plus what it yielded. – cstover May 24 '23 at 21:56
  • @PepijnKramer - Thanks for the tip(s)! This is a situation where I'm starting with something objectively very simple and working to build it up to something more robust by learning these C++ basics you mention + I'm lacking. Once I'm able to get over this hurdle, much more robust + technically-sound improvements will follow! – cstover May 24 '23 at 21:57
  • Tactical note: One thing I noticed after spending far too long in colleges and universities is that when an instructor says, "You can't use X in this program." What they really mean is, "Top marks go to the students who solve the problem by implementing their own version of X." If you implement with `std::vector`, then write your own `vector` and use it for the final submission, you'll have an easier time because the application logic and the container logic are cleanly separated and can be individually tested. It is always easier to test and verify two small things than one big one. – user4581301 May 24 '23 at 22:00
  • @user4581301 - I posted the updated approach as an answer below (with code formatting) + linked that answer to the original question, so that comments to that approach can be put there for ease of use, etc. I hope this is okay! – cstover May 24 '23 at 22:04
  • It's only OK if it answers the question. Currently it's sort of a half-answer. This is the sort of stuff you want to put in where the ask a question wizard was prompting for "What have you tried?", so I'd move it into the question and delete the answer. Alternately, you can update that answer once you have the ambiguity problem worked out so that it's a complete answer. – user4581301 May 24 '23 at 22:11
  • 1
    @user4581301 The fact that most teachers say don't use X is because they think they need to teach low level coding to explain datastructures (in other words they are not teaching C++ but something else). Or they just stopped updating their C++ knowledge for at least two decades – Pepijn Kramer May 24 '23 at 22:12
  • 1
    @PepijnKramer On my last run through school I got a Java assignment framed such that I could clearly see the C assignment it was based on and the pascal assignment the C assignment was based on. I thought it was funny, so I showed it to my boss at the time and he burst out laughing and then pointed out the signatures that showed it had descended from FORTRAN. Sometimes it's not "Teach the low-level ." it's "We can't afford the time and money to redesign the course material and haven't since 1965." – user4581301 May 24 '23 at 22:17
  • @user4581301 - Thanks for the Stack Overflow guidance! I updated the question + will delete my answer directly. – cstover May 24 '23 at 22:17
  • 1
    [Here's some documentation on the rules of Three, Five, and Zero](https://en.cppreference.com/w/cpp/language/rule_of_three) that were mentioned above. You absolutely have to know this ([And the idiom of RAII](https://en.cppreference.com/w/cpp/language/raii)) to write efficient and effective C++ code. – user4581301 May 24 '23 at 22:25
  • Tactical note: because a template all must be visible (with a few exceptions) to be usable, the benefit of separating the class definition from the method definitions all but vanishes. If you build all of your currently separated methods into the class definition many of your error messages will flat-out vanish. Here's a trivial example based on your templated version: https://godbolt.org/z/oc4cEh1rK – user4581301 May 24 '23 at 22:28
  • 1
    @cstover For the errors you are mentioning now in the question, add forward-declarations `template class SparseMatrix; template std::istream & operator>>(std::istream &is, SparseMatrix &s); template std::ostream & operator<<(std::ostream &os, SparseMatrix &s);` before the class definition and replace `operatorXX` with `operatorXX` in the `friend` declarations so that the `friend` declarations are friending the specializations of the template overloads for the operators instead of adding new overloads (which is a special behavior of `friend`). – user17732522 May 24 '23 at 22:28
  • 1
    (Or do what @user4581301 suggests. That's also valid. For the most part it is just a matter of style whether you want in-class definitions or not. The only significant difference is that the `<<` overloads will only be found via ADL in that case, which may however be desirable.) – user17732522 May 24 '23 at 22:30
  • The shown class violates the Rule Of Three. Whether `operator+` should or should not be `operator+=` is immaterial. Either way, this will always end in tears, until a copy constructor and an assignment operator gets correctly implemented. – Sam Varshavchik May 24 '23 at 22:32
  • @user4581301 - That solution works great! As a matter of style, I prefer to have my definitions outside of the class (I'm going to do that with constructor, destructor, and other rule of three/five functions now!), but it's awesome to know that this works! – cstover May 24 '23 at 22:35
  • 1
    @user17732522 - This is precisely what I was looking for! Do you want to write your comment as an answer so I can accept it? – cstover May 24 '23 at 22:36
  • 1
    @SamVarshavchik - Thanks for the tip! For these sorts of things, I basically do a bottom-up pass, so I start with the simplest implementation and then try to make it better / more technically sound. Clearly, I have some work ahead! Thank you for your advice. – cstover May 24 '23 at 22:36

0 Answers0