简体   繁体   中英

Parametrized constructor in virtually inheriting abstract class

I have a classical virtual inheritance diamond:

class A {
protected:
    A(const char *x) { ... }
}

class B: public virtual A {
protected:
    B(): A(NULL) { ... }
public:
    virtual void foo() = 0;
}

class C: public virtual A {
protected:
    C(): A(NULL) { ... }
public:
    virtual void bar() = 0;
}

class D: public B, public C {
public:
    D(const char *x): A(x) { ... }
    void foo();
    void bar();
}

I use NULL in B and C because as they're abstract classes, the A ctor will never be called in their constructurs. Is there a nicer way to do it, otherwise than specify NULL in the constructor or declare parameterless constructor in A ? I want the constructor to be called with a parameter, therefore the A() {} ctor should be allowed only in abstract classes.

You can change A to:

class A {
private:
    A() {};
    friend class B;
    friend class C;
protected:
    A(const char *x) { }
};

And then B(): A() {} and C(): A() {} would work, yet D(const char*): A() {} won't. But this is really awkward. I'd stick with the NULL you're using at the moment.

This is indeed an interesting case, though. I could think of no technical reason why you'd have to specify a constructor for A in B and C given they'll never be created, and whoever inherits them is going to initialize A anyway.

You can specify default argument in A(const char*)

class A {
protected:
    A(const char *x = 0) { ... }
                    ^^^^
};

However, this will also allow D to avoid A() .

I am closing this as the rigth answer probably does not exist. IMO the best workaround is the

struct AbstractPlaceholder {
    AbstractPlaceholder() {
        assert(false);
    }
};

class A {
protected:
    A(const AbstractPlaceholder &ap) {}
    A(const char *x) { ... }
};

A variation of your approach would be

class A {
protected:
    A(const char *x = NULL) {
      assert(x && "A mustn't be default constructed!");
    }
};

Thereby adding a more meaningful diagnostic.


However, you may want to explicitly allow x to be NULL (as a legal construction via C ), then you could use a Maybe type.

template <typename T> class Maybe {
  T const t; // must be default constructible!
  bool const invalid;
  public:
    Maybe() : t(), invalid(true) {}
    Maybe(T t) : t(t), invalid(false) {}
    bool nothing() const {
      return invalid;
    }
    T just() const {
      assert(!invalid);
      return t;
    }
};

Then you can change your constructor to

A::A(Maybe<const char*> mx) {
  // either
  assert(!mx.nothing());
  // or
  mx.just();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM