Нечетное поведение шаблона C ++ со статическим элементом vars -- c++ поле с участием templates поле с участием static поле с участием metaprogramming пол Связанный проблема

Odd C++ template behaviour with static member vars


2
vote

проблема

русский

Этот кусок кода предполагается рассчитать приближение к E (то есть математической константы ~ 2.71828183) при времени компиляции, используя следующий подход;

 <код> e1 = 2 / 1 e2 = (2 * 2 + 1) / (2 * 1) = 5 / 2   = 2.5 e3 = (3 * 5 + 1) / (3 * 2) = 16 / 6  ~ 2.67 e4 = (4 * 16 + 1) / (4 * 6) = 65 / 24 ~ 2.708 ... e(i) = (e(i-1).numer * i + 1) / (e(i-1).denom * i)   

Вычисление возвращается через <код> result статический элемент, однако после 2 итераций оно дает нулю вместо ожидаемого значения. Я добавил статическую функцию элементов f (), чтобы вычислить одинаковое значение, и это не проявляет ту же проблему.

 <код> #include <iostream> #include <iomanip>  // Recursive case.  template<int Iters, int Num = 2, int Den = 1, int I = 2> struct CalcE {     static const double result;     static double f () {return CalcE<Iters, Num * I + 1, Den * I, I + 1>::f ();} };  template<int Iters, int Num, int Den, int I> const double CalcE<Iters, Num, Den, I>::result = CalcE<Iters, Num * I + 1, Den * I, I + 1>::result;  // Base case.  template<int Iters, int Num, int Den> struct CalcE<Iters, Num, Den, Iters> {     static const double result;     static double f () {return result;} };  template<int Iters, int Num, int Den> const double CalcE<Iters, Num, Den, Iters>::result = static_cast<double>(Num) / Den; // Test it.  int main (int argc, char* argv[]) {     std::cout << std::setprecision (8);      std::cout << "e2 ~ " <<  CalcE<2>::result << std::endl;     std::cout << "e3 ~ " <<  CalcE<3>::result << std::endl;     std::cout << "e4 ~ " <<  CalcE<4>::result << std::endl;     std::cout << "e5 ~ " <<  CalcE<5>::result << std::endl;      std::cout << std::endl;     std::cout << "e2 ~ " <<  CalcE<2>::f () << std::endl;     std::cout << "e3 ~ " <<  CalcE<3>::f () << std::endl;     std::cout << "e4 ~ " <<  CalcE<4>::f () << std::endl;     std::cout << "e5 ~ " <<  CalcE<5>::f () << std::endl;      return 0; }   

Я проверил это с VS 2008 и VS 2010 и получить те же результаты в каждом случае:

 <код> e2 ~ 2 e3 ~ 2.5 e4 ~ 0 e5 ~ 0  e2 ~ 2 e3 ~ 2.5 e4 ~ 2.6666667 e5 ~ 2.7083333   

Почему <код> result не дает ожидаемых значений, тогда как <код> f() делает?

Согласно комментарий rotsor ниже, это работает с GCC, поэтому я думаю, что вопрос есть, я полагаясь на некоторое тип неопределенного поведения в отношении статического заказа инициализации, или это ошибка с Visual Studio?

Английский оригинал

This piece of code is supposed to calculate an approximation to e (i.e. the mathematical constant ~ 2.71828183) at compile-time, using the following approach;

e1 = 2 / 1 e2 = (2 * 2 + 1) / (2 * 1) = 5 / 2   = 2.5 e3 = (3 * 5 + 1) / (3 * 2) = 16 / 6  ~ 2.67 e4 = (4 * 16 + 1) / (4 * 6) = 65 / 24 ~ 2.708 ... e(i) = (e(i-1).numer * i + 1) / (e(i-1).denom * i) 

The computation is returned via the result static member however, after 2 iterations it yields zero instead of the expected value. I've added a static member function f() to compute the same value and that doesn't exhibit the same problem.

#include <iostream> #include <iomanip>  // Recursive case.  template<int Iters, int Num = 2, int Den = 1, int I = 2> struct CalcE {     static const double result;     static double f () {return CalcE<Iters, Num * I + 1, Den * I, I + 1>::f ();} };  template<int Iters, int Num, int Den, int I> const double CalcE<Iters, Num, Den, I>::result = CalcE<Iters, Num * I + 1, Den * I, I + 1>::result;  // Base case.  template<int Iters, int Num, int Den> struct CalcE<Iters, Num, Den, Iters> {     static const double result;     static double f () {return result;} };  template<int Iters, int Num, int Den> const double CalcE<Iters, Num, Den, Iters>::result = static_cast<double>(Num) / Den; // Test it.  int main (int argc, char* argv[]) {     std::cout << std::setprecision (8);      std::cout << "e2 ~ " <<  CalcE<2>::result << std::endl;     std::cout << "e3 ~ " <<  CalcE<3>::result << std::endl;     std::cout << "e4 ~ " <<  CalcE<4>::result << std::endl;     std::cout << "e5 ~ " <<  CalcE<5>::result << std::endl;      std::cout << std::endl;     std::cout << "e2 ~ " <<  CalcE<2>::f () << std::endl;     std::cout << "e3 ~ " <<  CalcE<3>::f () << std::endl;     std::cout << "e4 ~ " <<  CalcE<4>::f () << std::endl;     std::cout << "e5 ~ " <<  CalcE<5>::f () << std::endl;      return 0; } 

I've tested this with VS 2008 and VS 2010, and get the same results in each case:

e2 ~ 2 e3 ~ 2.5 e4 ~ 0 e5 ~ 0  e2 ~ 2 e3 ~ 2.5 e4 ~ 2.6666667 e5 ~ 2.7083333 

Why does result not yield the expected values whereas f() does?

According to Rotsor's comment below, this does work with GCC, so I guess the question is, am i relying on some type of undefined behaviour with regards to static initialisation order, or is this a bug with Visual Studio?

</div
           
       
       

Список ответов

1
 
vote
vote
Лучший ответ
 

Видимо, вы не можете зависеть от порядка инициализации статического элемента, по крайней мере, в VC ++.

Вот упрощенный пример:

 <код> #include <stdio.h>  template<int N> struct one {     static const int res; };  template<> struct one<0> {     static const int res; };  template<int N> const int one<N>::res = one<N-1>::res;  const int one<0>::res = 1;  int main() {     printf("%d ", one<3>::res);     printf("%d ", one<2>::res);     printf("%d ", one<1>::res);     printf("%d ", one<0>::res); }   

в VC ++ 2008, он производит:

 <код> 0 1 1 1   

в CodePad , производит:

 <код> 1 1 1 1   
 

Apparently you can't depend on the order of static member initialization, at least in VC++.

Here's a simplified example:

#include <stdio.h>  template<int N> struct one {     static const int res; };  template<> struct one<0> {     static const int res; };  template<int N> const int one<N>::res = one<N-1>::res;  const int one<0>::res = 1;  int main() {     printf("%d ", one<3>::res);     printf("%d ", one<2>::res);     printf("%d ", one<1>::res);     printf("%d ", one<0>::res); } 

In VC++ 2008, it produces:

0 1 1 1 

In codepad, it produces:

1 1 1 1 
</div
 
 
     
     
0
 
vote

Для того, что это стоит, результаты с g ++ 4.4.1:

 <код> e2 ~ 2 e3 ~ 2.5 e4 ~ 2.6666667 e5 ~ 2.7083333  e2 ~ 2 e3 ~ 2.5 e4 ~ 2.6666667 e5 ~ 2.7083333   
 

For what it's worth, results with g++ 4.4.1:

e2 ~ 2 e3 ~ 2.5 e4 ~ 2.6666667 e5 ~ 2.7083333  e2 ~ 2 e3 ~ 2.5 e4 ~ 2.6666667 e5 ~ 2.7083333 
</div
 
 
0
 
vote

C ++ не любит неинтегральные константы времени компиляции. Возможное решение - использовать рациональную арифметику:

 <код> #include <iostream> #include <iomanip>  template<int Iters, int Num = 2, int Den = 1, int I = 2> struct CalcE {     typedef CalcE<Iters, Num * I + 1, Den * I, I + 1> res;     enum { num = res::num, den = res::den };     static double g() { return static_cast<double>(num) / den; } };  template<int Iters, int Num, int Den> struct CalcE<Iters, Num, Den, Iters> {     enum { num = Num, den = Den };     static double g() { return static_cast<double>(num) / den; } };  int main (int argc, char* argv[]) {     std::cout << std::setprecision (8);      std::cout << "e2 ~ " <<  CalcE<2>::g() << std::endl;     std::cout << "e3 ~ " <<  CalcE<3>::g() << std::endl;     std::cout << "e4 ~ " <<  CalcE<4>::g() << std::endl;     std::cout << "e5 ~ " <<  CalcE<5>::g() << std::endl;     std::cout << "e5 ~ " <<  CalcE<6>::g() << std::endl;      return 0; }   
 

C++ doesn't like non-integral compile time constants. A possible solution is to use rational arithmetic:

#include <iostream> #include <iomanip>  template<int Iters, int Num = 2, int Den = 1, int I = 2> struct CalcE {     typedef CalcE<Iters, Num * I + 1, Den * I, I + 1> res;     enum { num = res::num, den = res::den };     static double g() { return static_cast<double>(num) / den; } };  template<int Iters, int Num, int Den> struct CalcE<Iters, Num, Den, Iters> {     enum { num = Num, den = Den };     static double g() { return static_cast<double>(num) / den; } };  int main (int argc, char* argv[]) {     std::cout << std::setprecision (8);      std::cout << "e2 ~ " <<  CalcE<2>::g() << std::endl;     std::cout << "e3 ~ " <<  CalcE<3>::g() << std::endl;     std::cout << "e4 ~ " <<  CalcE<4>::g() << std::endl;     std::cout << "e5 ~ " <<  CalcE<5>::g() << std::endl;     std::cout << "e5 ~ " <<  CalcE<6>::g() << std::endl;      return 0; } 
</div
 
 
   
   

Связанный проблема

-2  Как писать сортировать с неизвестным аргументом. Не может повторяться с void * /  ( How write sort with unknown argument cant iterate with void ) 
в этом <код> void* ic = b + j * sz; и это <код> void* jc = ic - sz; lines IDE, написание того, что выражение должно быть указателем на полный тип. Мне нужен...

-2  Umenu.cpp неопределенная справочная ошибка [дубликат]  ( Umenu cpp undefined reference error ) 
<в сторону CLASS = "S-NEWACTS S-WELTIVE__info JS-Post-New Imide MB16« Роль = «Статус»> Этот вопрос уже есть ответы здесь : ...

0  Новые и удаление контейнеров DLL  ( New and delete container dlls ) 
Я хочу зацепить новые и удалять операторы. Но я не могу найти оригинальные DLL, где проживают эти операторы. Я использовал msvcr90.dll, msvsr90d.dll, msvcrt.d...

10  Как я могу обнаружить доступ к файлу в Linux?  ( How can i detect file accesses in linux ) 
У меня есть куча потоков и приложений для обработки данных, которые я иногда нужно шпионить, то есть мне нужно знать, какие файлы они читают. Это в основном, ...

3  Проблема с помощью файлов makefile .gch вместо файлов  ( Problem with makefile making gch files instead of o files ) 
Итак, я делаю программу для проверки эффективности определенных структур данных. У меня есть все файлы .h, и я сделал очень ужасную Makefile, что, вероятно, н...

2  JNI не может найти класс, если аргументы JVM преобразуются из другого типа [Закрыто]  ( Jni cant find the class if the arguments of jvm are converted from other type ) 
<в сторону CLASS = "S-NEWACTS S-WELTIVE__info JS-Post-New Imide MB16« Роль = «Статус»> <Путь d = "M15 6.38A6.48 6.48 0 007.78. 04H-.02A6.49 6.49 0 002.05 ...

58  Пространства имен в C  ( Namespaces in c ) 
Есть ли способ (ab) использовать препроцессор c для эмуляции пространств имен в c ? Я думаю что-то по этим строкам: <код> #define NAMESPACE name_of_ns ...

0  Как скопировать файлы из установленного местоположения в изолированное хранение в Windows Phone 8  ( How to copy files from installed location to isolated storage in windows phone 8 ) 
Я разрабатываю приложение для WP8 с помощью Cocos2DX. Я не могу найти функцию копирования, которая помогает мне копировать файлы с установленного местоположен...

18  Как оценивается оператор «если (A && B)»?  ( How an if a b statement is evaluated ) 
<код> -1 Вопрос в том, что утверждение немедленно сломается, чтобы остальное, если был ложный. Будет б даже оценивать? Я спрашиваю это в том случае, если...

0  Что это значит, когда в GraphStudio нет ошибки при подключении два фильтра, но они не были подключены  ( What does it mean when in graphstudio there is no error when connection two filt ) 
Когда я пытаюсь подключить два фильтра в GraphStudio, которые не соответствуют, я обычно получаю код ошибки. Однако в моем случае, если я попытаюсь подключить...

1  Шаблон аргументирован вычет и объединение  ( Template argument deduction and unification ) 
<код> template<typename T> void f1(T t); template<typename T> void f2(const T t); template<typename T> void f3(T& t); template<typename T> void f4(const T...

7  Как очистить удаленные объекты в C ++  ( How do clean up deleted objects in c ) 
Возможно ли использовать память об удаленных объектах в C ++? Я хочу сделать это, чтобы воспроизвести Coredump в модульном тесте: <код> //Some member variab...

13  Как мне построить GCC на Mac?  ( How do i build gcc on a mac ) 
Я хотел бы построить последнюю версию GCC на Mac. У меня есть последний Xcode, но ищу некоторые функции C ++ 0x, которые находятся в более поздних версиях (фу...

0  Установите и получите значение разных участников класса в классе  ( Set and get the value of different class members in a class ) 
Я очень новичок в программировании C ++, и я написал простую классную программу для отображения имени и продолжительности проекта. <код> #include<iostream> ...

1  Eclipse C / C ++ не компилируют исходный код в подпункте  ( Eclipse c c not compiling source code in sub folders ) 
Использование ECLIPSE IDE для разработчиков C / C ++ (см. подробности ниже) Я пытаюсь реорганизовать свой код в подпункте. Однако, если я перемещаю файл исход...

Связанный проблема

-2  Как писать сортировать с неизвестным аргументом. Не может повторяться с void * / 
-2  Umenu.cpp неопределенная справочная ошибка [дубликат] 
0  Новые и удаление контейнеров DLL 
10  Как я могу обнаружить доступ к файлу в Linux? 
3  Проблема с помощью файлов makefile .gch вместо файлов 
2  JNI не может найти класс, если аргументы JVM преобразуются из другого типа [Закрыто] 
58  Пространства имен в C 
0  Как скопировать файлы из установленного местоположения в изолированное хранение в Windows Phone 8 
18  Как оценивается оператор «если (A && B)»? 
0  Что это значит, когда в GraphStudio нет ошибки при подключении два фильтра, но они не были подключены 
1  Шаблон аргументирован вычет и объединение 
7  Как очистить удаленные объекты в C ++ 
13  Как мне построить GCC на Mac? 
0  Установите и получите значение разных участников класса в классе 
1  Eclipse C / C ++ не компилируют исходный код в подпункте