0

Suppose I have a template class ComplexNumber that looks like this:

template<typename T>
class ComplexNumber {
public:
    ComplexNumber() : A(), B() {}
    ComplexNumber(const T& r, const T& i) : A(r), B(i) {}
    ~ComplexNumber(){}

    void setA(T A1);
    void SetB(T B1);
    T getA() const {return A;};
    T getB()const {return B;};

    ComplexNumber<T> operator+(const ComplexNumber<T> &C){
        return ComplexNumber<T>( A + C.getA(), B + C.getB());
    }

    ComplexNumber<T> operator -(const ComplexNumber<T> &C) {
        return ComplexNumber<T>(A - C.getA(), B - C.getB());
    };

    friend ostream & operator << (ostream &out, const ComplexNumber<T> &c)
    {
        out << c.A;
        out << "+i" << c.B << endl;
        return out;
    }

private:
    T A;
    T B;
};

And a main() that constructs these complex numbers and stores them into a std::vector:

ComplexNumber<int> complex1(10, 3);
ComplexNumber<int> complex2(2, 56);
ComplexNumber<int> complex3(3, 55);

vector<ComplexNumber<int>> testVector;
testVector.push_back(complex1);
testVector.push_back(complex2);
testVector.push_back(complex3);

If I want to sort testVector from highest to lowest, comparing the real parts and then comparing the imaginary ones if the real parts are equal, how would I go about doing that?

I am unable to use the standard std::sort() function. I would like to do this using a method or functor.

EDIT: Trying to add it to a function:

//This method is outside the scope of the ComplexNumber class

auto compare_by_magnitude = [](const auto& a, const auto& b) {
    return a.A*a.A + a.B*a.B < b.A*b.A + b.B*b.B;
};

void sortComplex(vector<ComplexNumber<int>> &c){
    std::sort(c.begin(),c.end(),compare_by_magnitude);
}

My error messages:

FAILED: complexNumber.exe 
cmd.exe /C "cd . && C:\PROGRA~1\JETBRA~1\CLION2~1.3\bin\mingw\bin\G__~1.EXE -g  CMakeFiles/complexNumber.dir/main.cpp.obj CMakeFiles/complexNumber.dir/ComplexNumber.cpp.obj -o complexNumber.exe -Wl,--out-implib,libcomplexNumber.dll.a -Wl,--major-image-version,0,--minor-image-version,0  -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 && cd ."
C:\Program Files\JetBrains\CLion 2021.3.3\bin\mingw\bin/ld.exe: CMakeFiles/complexNumber.dir/ComplexNumber.cpp.obj: in function `ComplexNumber<int>::~ComplexNumber()':
C:/PROGRA~1/JETBRA~1/CLION2~1.3/bin/mingw/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/bits/stl_heap.h:223: multiple definition of `sortComplex(std::vector<ComplexNumber<int>, std::allocator<ComplexNumber<int> > >&)'; CMakeFiles/complexNumber.dir/main.cpp.obj:C:/Users/elira/Desktop/complexNumber/complexNumber/ComplexNumber.h:51: first defined here
C:\Program Files\JetBrains\CLion 2021.3.3\bin\mingw\bin/ld.exe: CMakeFiles/complexNumber.dir/ComplexNumber.cpp.obj:C:/Users/elira/Desktop/complexNumber/complexNumber/ComplexNumber.h:48: multiple definition of `compare_by_magnitude'; CMakeFiles/complexNumber.dir/main.cpp.obj:C:/Users/elira/Desktop/complexNumber/complexNumber/ComplexNumber.h:48: first defined here
collect2.exe: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
axis100
  • 13
  • 4
  • there is no obvious way to compare two complex numbers via `<`. You need to define how you want to get them sorted. Eg by magnitude or by angle – 463035818_is_not_an_ai May 12 '22 at 18:41
  • 1
    btw there is `std::complex` that you can use – 463035818_is_not_an_ai May 12 '22 at 18:41
  • Well, for starters you have to define a comparison. How do you want them to be sorted? By absolute value? By phase angle?, by real value first, imaginary second? – Homer512 May 12 '22 at 18:43
  • @463035818_is_not_a_number - I'm sorry, I should have stated this. I would like to sort them from highest to lowest, comparing the real parts and then comparing the imaginary ones if the real parts are equal – axis100 May 12 '22 at 18:45
  • @Homer512 - I'm sorry, I should have stated this. I would like to sort them from highest to lowest, comparing the real parts and then comparing the imaginary ones if the real parts are equal – axis100 May 12 '22 at 18:45
  • 1
    Okay, then second question: You said you are unable to use standard sort. Does that mean you couldn't figure out how to use it or that you are not allowed to use it, e.g. in an assignment? – Homer512 May 12 '22 at 18:49
  • btw there are no template classes. `ComplexNumber` is a class template. And in the vector you have `ComplexNumber` which is a type like other types – 463035818_is_not_an_ai May 12 '22 at 18:51
  • @Homer512 I could not figure out how to use it. I kept getting errors with iterators. – axis100 May 12 '22 at 18:55
  • you should create a [mcve] to include it together with the compiler errors in the quesiton. – 463035818_is_not_an_ai May 12 '22 at 18:58
  • 2
    Your error message has nothing to do with sorting. It's a multiple definition error. https://stackoverflow.com/questions/41539456/understanding-what-causes-this-multiple-definition-error I guess you have it in a header when it should be in a .cpp file. Or declare t inline – Homer512 May 12 '22 at 19:16
  • If you want to keep the definition of `sortComplex` in the header, you need to make it `inline`; otherwise you'll violate ODR if you include the header in more than one translation unit. the lambda variable needs to be made `static` or `inline` for the same reason. – fabian May 12 '22 at 19:17
  • Does this answer your question? "[multiple definition error including c++ header file with inline code from multiple sources](//stackoverflow.com/q/212006/90527)", "[How do I use extern to share variables between source files?](//stackoverflow.com/q/1433204/90527)", "[Sorting complex numbers in a vector c++](//stackoverflow.com/q/28182887/90527)" – outis May 13 '22 at 03:55

1 Answers1

1

In order to work with std::sort(), your class needs at least operator< defined:

bool operator< (const ComplexNumber<T>& other) const {
    return A < other.A || (A == other.A && B < other.B);
}

It's also good practice to define operator==, but std::sort() will work with operator< alone.

When I make the above addition to your class, this main() sorts by real and then imaginary, in ascending order:

int main() {
    vector<ComplexNumber<float>> vc = { {4, 5}, {1, 3}, {4, 2} };
    copy(vc.begin(), vc.end(), ostream_iterator<ComplexNumber<float>>(cout, " "));
    cout << endl;
    sort(vc.begin(), vc.end());
    copy(vc.begin(), vc.end(), ostream_iterator<ComplexNumber<float>>(cout, " "));
    cout << endl;
}

But, you mentioned you want to sort in descending order. It's bad practice to define operator< in the reverse sense within the class, as you can imagine, so you can instead pass a comparison function to std::sort() that reverses the sense of the comparison:

sort(vc.begin(), vc.end(),
    [](const ComplexNumber<float> &f1, const ComplexNumber<float> &f2)
    { return ! (f1 < f2); }
);

These compilation and semantic concerns aside, when I look at the errors you're posting, I see a link error, and it appears that your class is being defined multiple times. This suggests that the include file which defines your class is missing an include guard.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Derek T. Jones
  • 1,800
  • 10
  • 18
  • 1
    `std::sort()` sorts in ascending order (low to high) by default, but the OP wants to sort in descending order instead (high to low). So you need to use a predicate in the 3rd parameter of `std::sort()` to handle that. For instance, implement `operator>` in the class, and then use `std::greater` for the predicate – Remy Lebeau May 12 '22 at 19:14
  • @RemyLebeau thanks for the catch! I updated my answer to show how to use a lambda for the comparison function. `std::greater` will work, but requires the OP to define `operator>`. – Derek T. Jones May 12 '22 at 19:27
  • 1
    Thank you so much for your help, Derek. You have no idea how much I appreciated this. I am fairly new to coding, so this saved my life trying to get this project done. Everything worked perfectly, just as you said. Thank you again. I wish I could upvote you a million times – axis100 May 12 '22 at 19:32
  • 1
    @DerekT.Jones another option, if the class only has `operator<` defined, without using a predicate, you can `sort()` the `vector` using reverse iterators instead, eg: `sort(vc.rbegin(), vc.rend());` Using `operator<`, `sort()` will put lower values towards the begin iterator, which in this case will be towards the end of the vector instead of the front. – Remy Lebeau May 12 '22 at 19:41