Ошибка сегментации, когда переменная-член типа std :: map не объявлена ​​во всех единицах компиляции

Когда я объявляю переменную-член типа 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.

Кто-нибудь может объяснить, почему это происходит?

-1

Решение

Вы нарушаете Правило единого определения, что приводит к неопределенному поведению. Это означает, что буквально все может случиться. Что включает в себя работу для одних задействованных типов, а не для других, или работу только при полной луне.

1

Другие решения

Проблема в том, что вы выполняете компиляцию в два этапа и только на первом этапе вы определяете 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, но это только случайно. У него все еще неопределенное поведение, так что может случиться что угодно.

2

По вопросам рекламы [email protected]