3

I have some code which is trying to do a kind of singleton polymorphism, like so:

// header
struct B
{
    virtual ~B() = default;
    virtual void F() = 0;

    static const B& Type1;
    static const B& Type2;
};

// cpp
struct D1 : B
{
    void F() override;
};

struct D2 : B
{
    void F() override;
};

const B& B::Type1 = D1();
const B& B::Type2 = D2();

// consumer
class Usage
{
public:
    Usage() : m_b(&B::Type1) {}

    void UseType1() { m_b = &B::Type1; }
    void UseType2() { m_b = &B::Type2; }
    void F() const { m_b->F(); }
private:
    const B* m_b;
};

Thus the consuming class always uses one of these instances, but the specific one is decided at runtime. (It's using references for polymorphism at the top level rather than pointers in order to properly delete the objects, but also avoid putting them on the heap as a smart pointer would.)

As I understand it, a const reference to a temporary is supposed to extend the lifetime of that temporary for the lifetime of the reference (with some caveats about lifetime generally ending at function exit or something like that). Since these particular references have static scope, they should exist for the lifetime of the process and thus keep the temporary around that long as well.

This code works as expected in VS2015, and also in VS2017 15.8.5 in the default C++14 compilation mode.

However, if I switch VS2017 to the C++17 compilation mode, then (without any compiler warnings) this crashes at runtime because some particular const B* points to an object which has a completely unrelated vtable -- ie. something has stomped on the memory which was supposed to be reserved for one of the instances. I assume that this means that the temporary was destroyed too early.

I can make this behave as expected by avoiding use of a temporary:

static const D1 GlobalType1;
static const D2 GlobalType2;
const B& B::Type1 = GlobalType1;
const B& B::Type2 = GlobalType2;

Is this a compiler bug or a standards violation in the code?

Miral
  • 12,637
  • 4
  • 53
  • 93
  • Could be related to the bug in [this thread](https://stackoverflow.com/questions/50402741/) – M.M Sep 26 '18 at 04:04
  • I would say this is a compiler bug, because what you are doing is correct. Also, Godbolt is your friend, so see what gcc and clang have to say on the matter. And for a compiler which fails to compile `constexpr void f(void) { }` compared to `constexpr void f() { }` as a regression, anything is possible. – Tanveer Badar Sep 26 '18 at 04:05
  • Godbolt doesn't actually run the generated code, so it doesn't have much of interest to say for a runtime error. And the VS2017 version it includes is too old. – Miral Sep 26 '18 at 04:38
  • But FWIW a complete example that crashes in C++17 mode in VS2017 can be found at https://gist.github.com/uecasm/108a8a495d9bef55fe34304dea5aa1e1. Expected behaviour is to print "Called D2." – Miral Sep 26 '18 at 04:39

1 Answers1

1

Since the conclusion in the comments seems to be that this is indeed a compiler bug, I've reported an issue.

Leaving the question open for a little while longer until this is concluded.

Miral
  • 12,637
  • 4
  • 53
  • 93