Когда я объявляю переменную-член типа std::map
в одном модуле компиляции, но не в другом, я получаю ошибку сегментации, когда содержащийся объект уничтожается. Когда я делаю то же самое с std::vector
, это работает просто отлично.
Это была определенно ошибка в моем случае, и я исправил ее, но мне все еще интересно, что вызвало сбой.
Вот код:
foo.hpp:
#ifdef DECLARE_MAP
#include <map>
#endif
#ifdef DECLARE_VECTOR
#include <vector>
#endif
#include <string>
class Foo {
public:
Foo();
private:
#ifdef DECLARE_MAP
std::map<std::string, std::string> m;
#endif
#ifdef DECLARE_VECTOR
std::vector<std::string> v;
#endif
};
foo.cpp:
#include "foo.hpp"
Foo::Foo()
{
}
main.cpp:
#include "foo.hpp"
int main()
{
Foo f;
}
Отлично работает с DECLARE_VECTOR
:
g++ -DDECLARE_VECTOR -c -o foo.o foo.cpp
g++ -o main main.cpp foo.o
Но вызывает ошибку сегментации с DECLARE_MAP
:
g++ -DDECLARE_MAP -c -o foo.o foo.cpp
g++ -o main main.cpp foo.o
Воспроизводится в clang 4.0 и gcc 4.4.7.
Кто-нибудь может объяснить, почему это происходит?
Вы нарушаете Правило единого определения, что приводит к неопределенному поведению. Это означает, что буквально все может случиться. Что включает в себя работу для одних задействованных типов, а не для других, или работу только при полной луне.
Проблема в том, что вы выполняете компиляцию в два этапа и только на первом этапе вы определяете DECLARE_MAP
или же DECLARE_VECTOR
, В результате получается две единицы перевода, которые выглядят так:
foo.cpp
переводческий блок:
// Contents of <map>
// Contents of <string>
class Foo {
public:
Foo();
private:
std::map<std::string, std::string> m;
};
int main()
{
Foo f;
}
main.cpp
переводческий блок:
// Contents of <map>
// Contents of <string>
class Foo {
public:
Foo();
private:
};
int main()
{
Foo f;
}
int main()
{
Foo f;
}
Как видите, каждая единица перевода имеет свое определение Foo
, Первый имеет Foo
содержащий std::map
а второй имеет это без.
Это нарушает следующее правило:
В программе может быть более одного определения типа класса […] при условии, что каждое определение отображается в отдельной единице перевода, и при условии, что определения удовлетворяют следующим требованиям. Учитывая такой объект по имени
D
определяется более чем в одной единице перевода, затем
каждое определение
D
должен состоять из одинаковой последовательности токенов; а также- […]
Если определения
D
не удовлетворяют этим требованиям, то поведение не определено.
Как видите, у вас неопределенное поведение. Да, это может работать, когда у вас есть DECLARE_VECTOR
, но это только случайно. У него все еще неопределенное поведение, так что может случиться что угодно.