1

I have a generic template class Vector. The intended use is for the user to specialize the generic Vector and define any custom operations. For example,

using IntVector = Vector<int>;

IntVector operator+ (const IntVector& a, const IntVector& b);

The issue is that there are some operations you can't define as free-functions, like IntVector& operator+= (const IntVector& b);. To work around this you can specialize through inheritance,

class IntVector : public Vector<int> {
public:
    IntVector& operator+= (const IntVector& b);
};

IntVector operator+ (const IntVector& a, const IntVector& b);

There is an issue with this though, the slice operator defined on the base template class returns a generic Vector_view instead of the specialized IntVector_view that supports operator+=. You would have to overload the slice operator with the same body as the base class, just a different return type, which is obnoxious.

This is a related question where it is recommends making a wrapper object, which is rather tedious for each concrete type. Is there any easy way to create a specialization where you can add new interfaces without having to redefine the type? If that is the only answer, I don't see a point in creating a generic base class.

ktb
  • 1,498
  • 10
  • 27
  • There is usually no point in creating a generic base class for mathematical operations. Templates are more appropriate for that usage. And instead of deriving class, you can use aliases. – Phil1970 Aug 20 '19 at 23:22
  • The only operators which can be overloaded as a member function but not as a free function are `=`, `()`, `[]`, and `->`. – aschepler Aug 20 '19 at 23:32

1 Answers1

1

You don't need to do anything special for +=.

Please correct me if I misunderstood the question. It's possible to define stuff like operator+= as a free function. For example, here's how you'd define it for std::vector:

#include <vector>

std::vector<int>& operator+=(std::vector<int>& a, std::vector<int> const& b) {
    if(b.size() != a.size())
        throw std::logic_error("SIN! SIN! SIN!");

    auto* b_scan = b.data();
    for(int& value : a) {
        value += *b_scan++; 
    }

    return a; 
}

Alternatively, if you wanted to avoid polluting the global namespace, you could define it in the same namespace as your Vector class, and it'd still work a expected because of Argument-Dependent Lookup.

What about other operators, like operator[]?

Unfortunately, something like operator[] does have to be a member function. We can allow users to define it's behavior by creating a general template that calls a user-provided function:

class Vector {

    // Stuff
   public:
    value_t& operator[] (int idx);
    const value_t& operator[] (int idx) const;
    // Add this
    auto operator[](T index) -> decltype(get_index(*this, index)) {
        return get_index(*this, index); 
    }
};

Users can control the behavior of operator[] by defining get_index. For example, we could provide a definition for get_index for Vector<int> like so:

Vector_view<int> get_index(Vector<int>& v, std::pair<int, int> p) {
    return {v.begin() + p.first, v.begin() + p.second}; 
}

This definition can go after the definition of Vector, it does not have to be forward-declared, and because operator[] is a template, the compiler will still find it. You can see a working example here.

Community
  • 1
  • 1
Alecto Irene Perez
  • 10,321
  • 23
  • 46
  • Hmmm, I did not know about `operator+=` being able to be free. I knew that `operator=` could not and assumed all inplace operators also could not. Is there a reference out there as to *why* certain methods cannot be free? A quick google didn't return much. – ktb Aug 21 '19 at 03:50
  • One more thing is how would one add more constructors? Perhaps `Vector` should be constructable from a `std::string`? – ktb Aug 21 '19 at 04:03
  • @ktb "_how would one add more constructors_?" - that sounds like a separate question. – Ted Lyngmo Aug 21 '19 at 14:09