C++17 и эталонное расширение статического временного срока службы

У меня есть код, который пытается выполнить своего рода одноэлементный полиморфизм, например:

// 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;
};

Таким образом, потребляющий класс всегда использует один из этих экземпляров, но конкретный определяется во время выполнения. (Он использует ссылки для полиморфизма на верхнем уровне, а не указатели, чтобы правильно удалять объекты, но также не помещать их в кучу, как это сделал бы интеллектуальный указатель.)

Насколько я понимаю, константная ссылка на временное значение должна продлевать время жизни этого временного объекта на время существования ссылки (с некоторыми оговорками о том, что время жизни обычно заканчивается при выходе из функции или что-то в этом роде). Поскольку эти конкретные ссылки имеют статическую область действия, они должны существовать в течение всего времени существования процесса и, таким образом, сохранять временные значения в течение этого времени.

Этот код работает, как и ожидалось, в VS2015, а также в VS2017 15.8.5 в режиме компиляции C++14 по умолчанию.

Однако, если я переключу VS2017 в режим компиляции С++ 17, то (без каких-либо предупреждений компилятора) произойдет сбой во время выполнения, потому что какой-то конкретный const B* указывает на объект, который имеет совершенно не связанную виртуальную таблицу, т.е. что-то растоптало память, которая должна была быть зарезервирована для одного из экземпляров. Я предполагаю, что это означает, что временное было уничтожено слишком рано.

Я могу заставить это вести себя так, как ожидалось, избегая использования временного:

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

Это ошибка компилятора или нарушение стандартов в коде?


person Miral    schedule 26.09.2018    source источник
comment
Может быть связано с ошибкой в ​​этой теме   -  person M.M    schedule 26.09.2018
comment
Я бы сказал, что это ошибка компилятора, потому что то, что вы делаете, правильно. Кроме того, Godbolt — ваш друг, так что посмотрите, что говорят по этому поводу gcc и clang. И для компилятора, который не может скомпилировать constexpr void f(void) { } по сравнению с constexpr void f() { } как регрессию, все возможно.   -  person Tanveer Badar    schedule 26.09.2018
comment
Godbolt на самом деле не запускает сгенерированный код, так что нет особого интереса говорить об ошибке во время выполнения. И версия VS2017, которую он включает, слишком старая.   -  person Miral    schedule 26.09.2018
comment
Но полный пример сбоя в режиме C++17 в VS2017 можно найти по адресу gist.github.com. /uecasm/108a8a495d9bef55fe34304dea5aa1e1. Ожидаемое поведение — напечатать Called D2.   -  person Miral    schedule 26.09.2018


Ответы (1)


Поскольку вывод в комментариях, похоже, заключается в том, что это действительно ошибка компилятора, я сообщил о проблеме.

Оставьте вопрос открытым на некоторое время, пока это не будет завершено.

person Miral    schedule 27.09.2018