2

I know that virtual functions should not be called either directly or indirectly in a constructor, but this code runs fine.
Is what I have here safe?

#include <iostream>
#include <string>

struct A {
    A (const std::string& name) {std::cout << name << std::endl;}
    virtual std::string tag() const = 0;
};

struct B: A {
    B() : A (tag()) {}
    virtual std::string tag() const override {return "B";}
};

int main() {
    B b; // Output gives "B\n"
}

If not, would the following (based on a comment) be a correct workaround?

// Replacement for class B:

struct B: A {
    B() : A (name()) {}
    virtual std::string tag() const override {return name();}
private:
    static std::string name() {return "B";}  // use static function
};
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
prestokeys
  • 4,817
  • 3
  • 20
  • 43

1 Answers1

5

Invoking virtual members in the constructor and/or destructor is generally ok.

It's a different game in the ctor initializer though, before all bases are initialized:

12.6.2 Initializing bases and members [class.base.init]

[...]
14 Member functions (including virtual member functions, 10.3) can be called for an object under construction. Similarly, an object under construction can be the operand of the typeid operator (5.2.8) or of a dynamic_cast (5.2.7). However, if these operations are performed in a ctor-initializer (or in a function called directly or indirectly from a ctor-initializer) before all the mem-initializers for base classes have completed, the result of the operation is undefined.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
  • Ok, this convinces me to go with my second design, using your recommendation of static function redirection. – prestokeys Sep 09 '14 at 23:34
  • @prestokeys: Yes, call the static function directly. – Deduplicator Sep 09 '14 at 23:37
  • 1
    Doesn't the final sentence just refer to typeid and dynamic_cast? And how can a ctor-initialiser possibly execute before the mem-initialisers of the base classes? – user207421 Sep 09 '14 at 23:59
  • @EJP: `MyClass() :MyBase(dynamic_cast(this)) {}` and similar. – Mooing Duck Sep 10 '14 at 00:05
  • @MooingDuck No virtual function call there. – user207421 Sep 10 '14 at 00:11
  • 3
    @EJP: It can execute if you explicitly specify base class initializer in the ctor initializer list. E.g. exactly as it is in the OP's original code sample. Since virtual function call `tag()` is used as an argument for base class initializer (in ctor initializer list), this virtual function will have to be called *before* base class initializer will have a chance to complete. Which is probably exactly what the above quote is intended to refer to. – AnT stands with Russia Sep 10 '14 at 05:37