2

I want to have an abstract class with a read and write method like:

template<typename Iterator>
virtual void read(uint64_t adr, Iterator begin, Iterator end) const = 0;    

template<typename Iterator>
virtual void write(uint64_t adr, Iterator begin, Iterator end) const = 0;

is there a way to achieve something like that?

Since there can't be a virtual template method, I thought about

  1. Get rid of the abstract class and use a template instead. In the template I would assume there is a read/write method taking an iterator.
  2. Make the abstract class a template too and pass the iterator type.

Is one of these ways a clean one? I'm on C++11 btw

yannick818
  • 125
  • 7

1 Answers1

1

Is one of these ways a clean one?

Yes: use static polymorphism instead of virtual functions. When a type is passed via a template, it is not erased and therefore needs no pre-generated virtual tables, so you can cause further template instantiation - that's what your use-case begs for.

Solution 1 (recommended)

So, if your abstract class is used as just an "interface", get rid of it and write read/write in the "implementations" directly:

struct Impl {
  template<typename Iterator> void  read(uint64_t, Iterator, Iterator) const { /* work */ }
  template<typename Iterator> void write(uint64_t, Iterator, Iterator) const { /* work */ }
};

and replace usages of your implementation classes through that abstract class with

template<typename Impl> void use_any_impl(Impl&&) { /* work */ }

However, if your abstract class contains some logic/data which is meant to be inherited, you can keep the class but get rid of anything virtual:

class Abstract {
protected: ~Abstract() = default;
public:    constexpr int inherit_me() const { return 42; }
};

class Impl: public Abstract { /* read() and write() same as above */ };

/* use_any_impl() same as above */

Solution 2

If your templated Iterators always (or can be reduced to) lead to a raw array storage (e.g. one provided by std::vector<unsigned char>::data), which seems reasonable for raw read/write operations, you can just use raw pointers:

virtual void  read(uint64_t, unsigned char*, unsigned char*) const = 0;
virtual void write(uint64_t, unsigned char*, unsigned char*) const = 0;
passing_through
  • 1,778
  • 12
  • 24
  • instead of `template void use_any_impl(Impl&&) { /* work */ }` could I use something like `template void use_any_impl(std::unique_ptr&&) { /* work */ }` – yannick818 Nov 03 '21 at 14:37
  • 1
    @yannick818 if you mean to pass the ownership of an `Impl` (held by a `unique_ptr`) to `use_any_impl`, accepting `std::unique_ptr` (`&&` not needed) is your solution. If you just lend it to use during the function call, stay with `Impl&&` and call the function like `use_any_impl(*unique)`. – passing_through Nov 03 '21 at 14:49
  • @yannick818 note that if your `unique_ptr` appeared with the abstract class solution to allow something like `auto unique_impl = std::make_unique(); Abstract* p = unique_impl.get()`, it isn't needed any more with Solution 1. Simple `Impl impl` does the job. EDIT FOR THE PREVIOUS COMMENT: for clarification about `&&` (rvalue reference VS forwarding reference), see https://stackoverflow.com/questions/40819058/is-this-a-forwarding-reference. – passing_through Nov 03 '21 at 14:53