Есть код
#include <functional>
#include <iostream>
template<typename T>
struct A{
static inline std::function<void()> func;
static inline int value;
static bool set(std::function<void()> func_, int value_){
func = func_;
value = value_;
return static_cast<bool>(A<int>::func);
}
static void call(){
std::cout << value << " : " << std::boolalpha << static_cast<bool>(A<int>::func) << std::endl;
func();
}
};
struct B{
static void func(){
}
static inline bool res = A<int>::set(B::func, 42);// || static_cast<bool>(A<int>::func);
};
int main(){
A<int>::call();
return 0;
}
для gcc (начиная с 7 и до последнего) в результате получается вывод
42 : false
terminate called after throwing an instance of 'std::bad_function_call'
what(): bad_function_call
т.е. объект в переменной func пустой
если раскомментировать вторую часть строки
static inline bool res = A<int>::set(B::func, 42) || static_cast<bool>(A<int>::func);
то вывод становится тем, который нужен (при этом раскоментированная часть даже не вызывается)
42 : true
0
при этом в clang любой версии все нормально и с закоментированным куском.
Что не так с кодом или с gcc?
Спасибо HolyBlackCat
Перемещение static func переменной в функцию отлично помогло. Код ниже работает как ожидалось
#include <functional>
#include <iostream>
template<typename T>
struct A{
static std::function<void()>& func(){
static std::function<void()> func;
return func;
}
// static inline std::function<void()> func;
static bool set(std::function<void()> func_){
func() = func_;
return static_cast<bool>(func());
}
static void call(){
std::cout << std::boolalpha << static_cast<bool>(A<int>::func()) << std::endl;
func()();
}
};
struct B{
static void func(){
std::cout << "func" << std::endl;
}
static inline bool res = A<int>::set(B::func);// || static_cast<bool>(A<int>::func);
};
int main(){
A<int>::call();
return 0;
}
Что не так с кодом или с gcc?
В комментариях правильно пишут, дело в порядке инициализации.
В обоих случаях правильная работа не гарантируется.
Судя по cppreference, инициализация статических переменных в шаблонах классов (когда переменные не были явно специализированы) выполняется в произвольном порядке, в том числе относительно всей остальной (неконстантной) инциализации глобальных/статических переменных.
Так что компилятор вполне может инициализировать B::res
до A<int>::func
, что GCC и делает в первом случае.
То, что GCC меняет порядок инициализации, когда понимает, что B::res
зависит от A<int>::func
(второй случай) - не более, чем любезность разработчиков GCC.
Если B::res
инициализируется до A<int>::func
, то это приводит к использованию func
до его инициализации и неопределенному поведению.
Чтобы не отстреливать себе постоянно ногу порядком инициализации, можно помещать чувствительные к порядку инициализации переменные в функции:
В class A
:
static std::function<void()> &func()
{
static std::function<void()> ret;
return ret;
}
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Хотите улучшить этот вопрос? Переформулируйте вопрос так, чтобы он был сосредоточен только на одной проблеме
Есть некоторая таблица в которую из базы данных через промежуточную структуру подгружаются данные(вектор объектов класса)В структуре данные...
1) Код должен быть или кроссплатформенный, или 2а варианта кода (под Windows и Linux)