2015-08-19 3 views
8

Я экспериментировал с использованием рекурсии шаблона для создания вложенных структур POD, и я наткнулся на какое-то поведение, которого я не ожидал. Вот упрощенный тест:Инициализация templated, recursive, POD struct

#include <cstddef> 

template<std::size_t size> 
struct RecursiveStruct { 
public: 
    template <std::size_t start, std::size_t length> 
    struct Builder { 
     static const Builder value; 
     static const size_t mid = start + length/2; 
     static const size_t end = start + length; 
     Builder<start, mid - start> left; 
     Builder<mid, end - mid> right; 
    }; 

    template <std::size_t start> 
    struct Builder<start, 1> { 
     static const Builder value; 
     int data; 
    }; 

    static const Builder<0, size> result; 
}; 

template<std::size_t size> 
const typename RecursiveStruct<size>::template Builder<0, size> 
     RecursiveStruct<size>::result = Builder<0, size>::value; 

template<std::size_t size> 
template<std::size_t start, std::size_t length> 
const typename RecursiveStruct<size>::template Builder<start, length> 
     RecursiveStruct<size>::Builder<start, length>::value 
      = { Builder<start, mid - start>::value, Builder<mid, end - mid>::value }; 

template<std::size_t size> 
template <std::size_t start> 
const typename RecursiveStruct<size>::template Builder<start, 1> 
     RecursiveStruct<size>::Builder<start, 1>::value = { 5 }; 

//////////////////////////////////////////////////////// 

#include <iostream> 

using std::cout; 
using std::endl; 
using std::size_t; 

int main() { 
    cout << RecursiveStruct<1>::result.data << endl; 
    cout << RecursiveStruct<2>::result.left.data << endl; 
    return 0; 
} 

Я ожидал бы этот код выхода

5 
5 

В самом деле, что это то, что создается при компиляции с GCC 4.8.4 и 5.1.

Однако компиляции либо с Clang (3.5 или 3.7) или Visual Studio 2010, а не приводит к

5 
0 

Является ли мой код или мое понимание этого плохого в некотором роде, или сделать Clang и Visual Studio как-то так есть ошибки, которые приводят к тому же ошибочному результату?

ответ

3

Я думаю, что оба компилятора соответствуют, поскольку порядок инициализации статических переменных не указан. Ясное заявление, приходит из записки в [basic.start.init]:

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

inline double fd() { return 1.0; } 
extern double d1; 
double d2 = d1; // unspecified: 
// may be statically initialized to 0.0 or 
// dynamically initialized to 0.0 if d1 is 
// dynamically initialized, or 1.0 otherwise 
double d1 = fd(); // may be initialized statically or dynamically to 1.0 

-end примечание]

В нашем случае, Builder<start, 1>::value статически инициализируется, но все остальное динамически инициализирован - так это не определено, чтобы полностью инициализирован Builder<start, 1>::value используется или не.

Обойти это можно использовать конструкцию на первом использовании идиомы и сделать что-то подобное (я взял на себя смелость вытаскивания Builder из RecursiveStruct для простоты - он демонстрирует такое же поведение, так или иначе):

template <std::size_t start, std::size_t length> 
struct Builder 
{ 
    static const size_t mid = start + length/2; 
    static const size_t end = start + length;  

    static const Builder value() { 
     static const Builder value_{ 
      Builder<start, mid - start>::value(), 
      Builder<mid, end - mid>::value() 
     }; 
     return value_; 
    } 

    Builder<start, mid - start> left; 
    Builder<mid, end - mid> right; 
}; 

template <std::size_t start> 
struct Builder<start, 1> { 
    static const Builder value() { 
     static const Builder value_{5}; 
     return value_; 
    } 

    int data; 
}; 

template<std::size_t size> 
struct RecursiveStruct { 
public: 
    static const Builder<0, size> result; 
}; 

template <std::size_t size> 
const Builder<0, size> RecursiveStruct<size>::result = Builder<0, size>::value(); 

Этот печатает 5 на обоих компиляторах.

+0

Если я неправильно понял, я не думаю, что эта заметка будет применяться здесь. Я понимаю, что неопределенная инициализация применяется к динамической инициализации. То есть инициализация происходит при запуске программы. Насколько я понимаю, причина, по которой данный пример не определен, заключается в том, что 'fd()' может или не может быть встроен компилятором, поэтому 'd1' может быть динамически или статически инициализирован, поэтому' d2' не указан. В моем случае все это const POD, поэтому все должно быть статически инициализировано. – rkjnsn

+0

Это может быть неуказанное поведение, но если это так, я не думаю, что это справочная нота, которая делает это так. – rkjnsn

+0

@rkjnsn Статическая инициализация требует инициализации с постоянным выражением - поэтому некоторые из ваших объектов динамически инициализируются. – Barry

Смежные вопросы