Почему статический элемент const char * template не инициализирован

У меня есть некоторый код шаблона C ++ 11, который я пытаюсь перенести на Visual C ++ Compiler 2015. Оригинальный код прекрасно работает, однако мне нужно переписать его, чтобы обойти проблемы с constexpr.

Оригинальный код (упрощенный пример)

#include <iostream>

struct String
{
static constexpr const char * value{ "STRING" };
};

template<typename Base>
class Derived
{
public:
static constexpr const char * value{ Base::value };
};

template<typename BarType>
struct Foo
{
static constexpr const char * value{ BarType::value };
};

using Bar = Derived<String>;
using FooBar = Foo<Bar>;

int main()
{
std::cout << "FooBar::value = " << FooBar::value << std::endl;
}

Это печатает:

FooBar::value = STRING

Однако, когда я переписываю, некоторые статические переменные не инициализируются. Даже если он компилируется просто отлично.

Портированный код (не работает)

#include <iostream>

struct String
{
static const char * value;
};
const char * String::value = "STRING";

template<typename Base>
class Derived
{
public:
static const char * value;
};
template<typename Base>
const char * Derived<Base>::value = { Base::value };

template<typename BarType>
struct Foo
{
static const char * value;
};
template<typename BarType>
const char * Foo<BarType>::value = { BarType::value };

using Bar = Derived<String>;
using FooBar = Foo<Bar>;

int main()
{
std::cout << "FooBar::value = " << FooBar::value << std::endl;
}

Это печатает:

// nothing (Segmentation fault)
  1. Почему это происходит?

  2. Как мне исправить / обойти это?

Это может быть воспроизведено в Clang и Visual-C ++, однако GCC печатает FooBar::value = STRING также во втором примере.

Обновить: Рабочий раствор

По предложению @ serge-ballesta. Я предпочитаю это решение, так как оно очень похоже на оригинальный код. Легко наносится и снова удаляется при добавлении членов constexpr в VS.

4

Решение

Я думаю, что проблема исходит от [basic.start.init]:

Динамическая инициализация нелокальной переменной со статической продолжительностью хранения неупорядоченный если переменная является неявно или явно конкретизированной специализацией

Инициализация Derived<Base>::value а также Foo<BarType>::value не являются статической инициализацией — потому что правая часть не является константным выражением. Это делает его динамической инициализацией. Поскольку переменные являются шаблонными специализациями, инициализация неупорядоченный — то есть не существует явно определенного порядка для двух values.

Таким образом, у нас есть два возможных заказа. Действительный:

Derived<Base>::value ==> 0
Foo<BarType>::value ==> 0
Derived<Base>::value ==> Base::value
Foo<BarType>::value ==> BarType::value

И недействительный:

Derived<Base>::value ==> 0
Foo<BarType>::value ==> 0
Foo<BarType>::value ==> BarType::value
Derived<Base>::value ==> Base::value

Если Derived<Base>::value сначала инициализируется, затем Foo<BarType>::value будет указывать на "STRING", В противном случае, если последний инициализируется первым, он будет инициализирован как 0, Ошибка сегментации, которую вы видите в результате попытки потоковой передачи нулевого символьного указателя.

2

Другие решения

@ Барри объяснил причину проблемы.

Возможным обходным решением будет принудительный порядок инициализации. Как String это не шаблонный класс, String::value будет правильно инициализирован (статически) до динамической инициализации.

Я могу представить 2 способа:

  1. Добавьте явный метод init к Foo вместо зависимости от автоматической динамической инициализации:

    ...
    template<typename BarType>
    struct Foo
    {
    static const char * value;
    static void init() {
    Foo::value = BarType::value;
    }
    };
    
    template<typename BarType>
    const char * Foo<BarType>::value;
    
    using Bar = Derived<String>;
    using FooBar = Foo<Bar>;
    
    int main()
    {
    FooBar::init();
    std::cout << "FooBar::value = " << FooBar::value << std::endl;
    }
    
  2. Делать value функция в Foo:

    ...
    template<typename BarType>
    struct Foo
    {
    static const char * value() {
    return BarType::value;;
    }
    };
    
    using Bar = Derived<String>;
    using FooBar = Foo<Bar>;
    
    int main()
    {
    std::cout << "FooBar::value = " << FooBar::value() << std::endl;
    }
    
1

По вопросам рекламы [email protected]