Ваша проблема не имеет ничего общего с включением или связыванием файлов. components
- это шаблон переменной, а не фактическая переменная. Для каждого типа вы создаете его (в вашем случае float
и int
), явно или неявно, компилятор создаст для вас переменную.
Итак, что у вас в основном есть:
- с
World.add<int>(...)
в main.cpp вы создаете переменную World::components<int>
и заполняете ее значениями. - с
layer.add(...)
в main.cpp вы вызываете World::add<float>
в layer.cpp, который создастпеременная World::components<float>
Обе переменные, это то, как работают шаблоны. То же самое происходит с вашими шаблонами функций в World
. Для каждого типа создаются шаблоны, компилятор создаст для вас новую функцию.
Редактировать после комментариев :
Возможно, я все еще что-то не так понял, но
layer.get(i)
и world::get<float>(i)
дают мне то же значение, добавленное с layer.add(i/5.f)
, так что оно работает, как должно, нене так ли?
Оба дают значения в World::components<float>
, тогда как World::get<int>(i)
дает мне значения в World::components<int>
Вывод для программы (после исправления некоторых небольших ошибок):
get<int>:0
Layer::get:0
get<float>:0
get<int>:1
Layer::get:0.2
get<float>:0.2
get<int>:2
Layer::get:0.4
get<float>:0.4
get<int>:3
Layer::get:0.6
get<float>:0.6
get<int>:4
Layer::get:0.8
get<float>:0.8
Второе редактирование (копание) :
Так как что-то должно или не должно работать (и не только иногда) я начал копать. Действительно, компилятор создает два экземпляра World::components<float>
. После
g++ -o prog layer.cpp main.cpp
и
nm prog | grep components
я получаю
0000000000407210 b _ZN5World12_GLOBAL__N_110componentsIfEE
0000000000407230 b _ZN5World12_GLOBAL__N_110componentsIfEE
00000000004071f0 b _ZN5World12_GLOBAL__N_110componentsIiEE
, что говорит мне, что два components<float>
и один component<int>
находятся в разделе BSSобъектный файл.
В зависимости от порядка компиляции, программа работает либо как ожидалось, или выдает ошибку сегментации. Вышеуказанное состояние заказа работает,
g++ -o prog main.cpp layer.cpp
приводит к ошибке сегментации, о которой сообщает op. Не проверяя доступ ко всем трем компонентам в скомпилированном файле, я предполагаю, что компиляция main.cpp создает версию вектора float
и int
, компиляция layer.cpp создает вторую версию float
вектор. Первые два инициализируются в main.cpp, а третий остается пустым, что приводит к неопределенному поведению при доступе к нему с помощью оператора []
позже. Кажется, если компилировать наоборот, компоновщик разрешает каждый доступ к components<float>
к одному и тому же экземпляру в разделе BSS, так что программа на самом деле работает нормально, но, вероятно, выдает неожиданное / неопределенное поведение при других обстоятельствах (например, есливторой экземпляр components<float>
доступен как-то еще.
Итак, необходимо решение. Я изменил world.hpp на
#pragma once
#include <vector>
namespace World {
template<typename T>
struct Comp {
static std::vector<T> components ;
};
template<typename T>
T get(int i) {
return Comp<T>::components[i] ;
}
template<typename T>
void add (T elm) {
Comp<T>::components.push_back(elm) ;
}
}
template<typename T>
std::vector<T> World::Comp<T>::components;
, который работает независимо от порядкакомпиляции / связывания.
Третье редактирование (примечания): Кстати. Я прочитал в ваших комментариях ниже вашего вопроса, что вы хотите скрыть components
извне или ограничить доступ к нему. Если это ваша цель, вы также можете сделать следующее:
- Изменить
World
из пространства имен на класс - Объявить
components
как private
и static
член - Объявите две ваши функции как
public
и static
В этом случае вам также нужно определить свой закрытый статический член, как я это делал в примере выше (последние две строки).