-1

The following example produces this run-time error:

pure virtual method called
terminate called without an active exception

e.g.

template<class DERIVED>
struct Base {
    int val;
    Base(int i) : val(static_cast<DERIVED*>(this)->init_val(i)) {}
    virtual int init_val(int i) = 0;
};
struct Derived : public Base<Derived> {
    Derived(int i): Base<Derived>(i) {}
    int init_val(int i) override {return i;}
};
int main(){
    Derived d{100};
    cout << d.val;
}

On the other hand, this next example works; printing 100 as expected:

template<class DERIVED>
struct Base {
    int foo(int i){return static_cast<DERIVED*>(this)->bar(i);}
    virtual int bar(int i) = 0;
};
struct Derived : public Base<Derived> {
    int bar(int i) override {return i;}
};
int main(){
    Derived d;
    cout << d.foo(100);
}

Perhaps the most interesting is that it works if you don't declare init_val() as a virtual/overriden function. It has been suggested that this is duplicate. My understanding of the potentially duplicated question is that virtual functions can't be called in a base constructor because the child doesn't exist yet. In light of the fact that the code works when init_val is not virtual the proposed duplicated question does not apply here.

Clang and g++ produce the same behavior using c++17.

Why does calling the typecast-virtual-function within the base's constructor fail when calling it in a method works?

UPDATE: The suggestion by immibis worked. i.e. changing the call to virtual base in the constructor to this:

    static_cast<DERIVED*>(this)->DERIVED::init_val(i)

Is this "safe"? Why does it work?

user9816683
  • 105
  • 4
  • @hlt I don't think this is a duplicate. See the updated question. – user9816683 May 21 '18 at 03:32
  • 1
    If you call virtual functions in a base class constructor you call the base's version - that is how virtual functions work. Did you try calling the derived class's version *specifically* using something like `static_cast(this)->DERIVED::bar(i)`? – user253751 May 21 '18 at 03:35
  • @hlt CRTP provides a different context than is seen in the proposed duplicated question. Please remove the possible duplicate header--no one is going to read the question. – user9816683 May 21 '18 at 03:36
  • 2
    @user9816683 I disagree, the duplicate exactly answers your question, as it's written at the end. The CRTP stuff is irrelevant for answering that particular question. If you call a virtual function in a base class constructor, you get the base class implementation, CRTP or not. In particular I would like to point out that you're ***not*** asking how the CRTP can be used to avoid this limitation. – user253751 May 21 '18 at 03:41
  • @immibis that works, thank you. If I understand what you're saying, this works because the base ctor was calling its own method instead of the derivative's but now we're telling it to explicitly call the derivative's. Is that correct? – user9816683 May 21 '18 at 04:01
  • @immibis A follow up: everything I recall about child virtual methods in base ctors indicates the non-existance of the child at base compile time? Is that correct and if so why doesn't that seem to apply here (i.e. since your suggestion worked)? – user9816683 May 21 '18 at 04:03
  • @hlt I have updated the question again with immibis' suggestion. The CRTP shown in the proposed duplicated question does not cover this. My question has evolved thanks to this discussion and is now asking something not covered in the proposed duplicate. Please remove the header so others can benefit from this information. – user9816683 May 21 '18 at 04:17

1 Answers1

1

Is this "safe"?

No. You are calling a member function on an object that does not yet exist. At the point when the base class constructor is being called, the derived class has not come into being. As such, you cannot call member functions on it.

It only "works" by accident; undefined behavior is undefined.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982