Странная проблема с внешними константами в конструкторе шаблонных классов

#c #static-order-fiasco

#c #статический порядок -фиаско

Вопрос:

У меня есть следующие три исходных файла:

main.cc:

 #include "constants.h"
#include <cstdlib>
#include <iostream>

template <std::size_t n> class Foo {
    public:
    static const double bar;
    Foo();
};

template <std::size_t n> const double Foo<n>::bar = 4*pi;

template <std::size_t n> Foo<n>::Foo() { std::cout << "ctor: " << bar << std::endl; }

const Foo<42> baz;

int main(void)
{
    std::cout << "main(): " << Foo<42>::bar << std::endl;
    std::cout << "main(): " << baz.bar << std::endl;
    return 0;
}
  

constants.cc:

 #include "constants.h"

const double pi = 3.1415;
  

constants.h:

 #ifndef CONSTANTS_H
#define CONSTANTS_H

extern const double pi;

#endif
  

и когда я компилирую и связываю все и запускаю исполняемый файл, я получаю:

 ctor: 0
main(): 12.566
main(): 12.566
  

Что дает? Почему конструктор Foo<42> не может видеть правильное значение pi? Похоже, это происходит только в том случае, если Foo является шаблоном, и только если pi определен в другом файле.

Как бы то ни было, я использую g (Ubuntu 7.5.0-3ubuntu1 ~ 18.04) 7.5.0. Все --std={c ,gnu }{98,03,11,14,17} дают одинаковые результаты.

Спасибо за ваши ответы.

Комментарии:

1. сбой порядка статической инициализации.

2. Исправить это, создав pi a constexpr ? Или используйте вместо этого boost pi (чтобы избежать фиаско).

3. Также смотрите эти математические константы доступны в стандартной библиотеке

4. Я думаю, вы столкнулись с «неупорядоченной динамической инициализацией» (см. Здесь ). Существует неожиданный (по крайней мере, для меня) особый случай (т. Е. ошибка, ожидающая своего появления) для инициализации «элементов статических данных шаблона класса […], которые явно не специализированы».

5. Если вы можете использовать c 20, constinit легко решит эту проблему.