1

I have a class baseNoCrtpIterator that holds one member baseNoCrtp _base, among others. Class baseNoCrtp has several simply derived classes derived[n]NoCrtp, where [n] denotes a number 1,2,... These classes do not add any data members to baseNoCrtp, only some functionality.

I want that, during construction of a baseNoCrtpIterator, member _base is assigned one of derived[n]NoCrtp, depending on another value dclass passed to the constructor.

Is that possible?

I tried a constructor like

baseNoCrtpIterator::baseNoCrtpIterator(const int dclass) :
    _base(dclass)       // Point #8
{
    if (dclass == 0) {
        derived1NoCrtp* d1n = new derived1NoCrtp(0);
        _base = *d1n;        // Point #9
    }
    else if (dclass == 1) {
        derived2NoCrtp* d2n = new derived2NoCrtp(1);
        _base = *d2n;
    } else {
        cout << "Unknown derived class: " << dclass << endl;
        throw runtime_error("Unknown derived class");
    }
};

This doesn't work, and I probably have to fix #8 (I guess I have to remove it from the initializer list), and #9, since I don't have operator= (I could write it, but I guess it doesn't solve my problem).

Note: I guess CRTP might be an alternative. But I see a couple of problems:

  1. All uses of baseNoCrtpIterator have to be replaced by baseNoCrtpIterator<T>. Thus, downstream, all functions, classes, etc., that use baseNoCrtpIterator will have to be "templetized". This means huge changes in existing code.
  2. I would not be able to hold all objects of class baseNoCrtp and derived in a single container. That is part of the changes mentioned above.

TL;DR

I have two hierarchies of (base + n simply derived) classes. One is a "main" hierarchy, and the other is an "iterator" hierarchy, meant to iterate over the main.

The base class for the iterators refers to the base class for the main in #1

class baseNoCrtpIterator {
public:
    baseNoCrtpIterator(const baseNoCrtp& base, const int dclass);
    virtual ~baseNoCrtpIterator() {};

    const baseNoCrtp& operator*() const { return *operator->(); };
    const baseNoCrtp* operator->() const { return &_base; };
private:
    baseNoCrtp _base;   // Point #1
    int _index;
};

and its constructor is

baseNoCrtpIterator::baseNoCrtpIterator(const structure& s, const int dclass) :
    _base(s, dclass),
    _index(0) { };

where dclass indicates the derived class of _base (that is not enough, as will be shown later). The derived iterator classes are like this (derived1... is associated with dclass=0, and so on)

class derived1NoCrtpIterator : public baseNoCrtpIterator {         // derived1...
public:
    derived1NoCrtpIterator(const baseNoCrtp& base) : baseNoCrtpIterator(base, 0) {};   // ... dclass=0
    virtual ~derived1NoCrtpIterator() {};

    const derived1NoCrtp& operator*() { return *operator->(); }
    const derived1NoCrtp* operator->() {                             // Point #2
        const baseNoCrtp* bnp = baseNoCrtpIterator::operator->();    // Point #3
        return static_cast<const derived1NoCrtp*>(bnp);              // Point #4
    }
};

The problem is when using something like

for (derived1NoCrtpIterator d1ni(sconst); ...) {
    const derived1NoCrtp& d1nr = *d1ni;   // Point #5
    ...

Point #5 leads to #2. In #3 we extract a pointer to member baseNoCrtp. But then the static_cast downcast in #4 is undefined behavior. This could be solved if the constructor baseNoCrtpIterator::baseNoCrtpIterator assigned to baseNoCrtp objects of the derived classes derived1NoCrtpIterator, etc., instead of the base class.

I tried to replace the constructor with something like

baseNoCrtpIterator::baseNoCrtpIterator(const structure& s, const int dclass) :
    _base(s, dclass),        // Point #8
    _index(0) {
    if (dclass == 0) {
        derived1NoCrtp* d1n = new derived1NoCrtp(s, 0);
        _base = *d1n;        // Point #9
    }
    else if (dclass == 1) {
        derived2NoCrtp* d2n = new derived2NoCrtp(s, 1);
        _base = *d2n;
    } else {
        cout << "Unknown derived class: " << dclass << endl;
        throw runtime_error("Unknown derived class");
    }
};

This doesn't work, and I probably have to fix #8 (I guess I have to remove it from the initializer list), and #9, since I don't have operator= (I could write it, but I guess it doesn't solve my problem).

How can I achieve this assignment to _base of objects of derived classes, depending on dclass?

Since this is a boiled down example of a very large code that I have to fix, I am in principle not interested in other methods that require extensive modifications of the code. For instance, if CRTP might be a solution, it probably involves huge changes. I know the current way might be a somewhat contrived form of iterating the main hierarchy, but this is what I received.


The "main" classes are (here it is not relevant what structure is):

class baseNoCrtp {
public:
    baseNoCrtp(const structure& s, const int dclass) :
        _structure(s), _derivedClass(dclass) { };
    virtual ~baseNoCrtp() {};
public:
    int getDerivedClass() const { return _derivedClass; }
    const structure& getStructure() const { return _structure; };
protected:
    const structure& _structure;
private:
    friend class baseNoCrtpIterator;
    int _derivedClass;
};

class derived1NoCrtp : public baseNoCrtp {
public:
    derived1NoCrtp(const structure& s) : baseNoCrtp(s, 0) {};
    virtual ~derived1NoCrtp() {};
    // Add whatever extra methods
};

1 Answers1

0

It seems this is not possible, see also this.

The "workaround" is to change the type of member _base, to baseNoCrtp*, so it can point to objects of any derived class.