C++ 未在所有编译单元中声明std::map类型的成员变量时出现分段错误

C++ 未在所有编译单元中声明std::map类型的成员变量时出现分段错误,c++,map,segmentation-fault,destructor,member-variables,C++,Map,Segmentation Fault,Destructor,Member Variables,当我在一个编译单元而不是另一个编译单元中声明类型为std::map的成员变量时,当包含的对象被破坏时,我会得到一个分段错误。当我对std::vector做同样的操作时,它工作得很好 在我的案例中,这肯定是一个bug,我已经修复了它,但我仍然想知道是什么导致了崩溃 代码如下: foo.hpp: #ifdef DECLARE_MAP #include <map> #endif #ifdef DECLARE_VECTOR #include <vector> #endif #in

当我在一个编译单元而不是另一个编译单元中声明类型为
std::map
的成员变量时,当包含的对象被破坏时,我会得到一个分段错误。当我对
std::vector
做同样的操作时,它工作得很好

在我的案例中,这肯定是一个bug,我已经修复了它,但我仍然想知道是什么导致了崩溃

代码如下:

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
};
main.cpp:

#include "foo.hpp"

int main()
{
    Foo f;
}
适用于
DECLARE\u VECTOR

g++ -DDECLARE_VECTOR -c -o foo.o foo.cpp
g++ -o main main.cpp foo.o
但导致
DECLARE\u MAP
出现分段错误:

g++ -DDECLARE_MAP -c -o foo.o foo.cpp
g++ -o main main.cpp foo.o
可在clang 4.0和gcc 4.4.7中复制


有人能解释为什么会发生这种情况吗?

您违反了一个定义规则,导致了未定义的行为。这意味着任何事情都可能发生。这包括为涉及的某些类型而不是其他类型工作,或者只在满月时工作。

问题是,编译分两步进行,只有在第一步定义
声明映射
声明向量
。这将导致两个翻译单元如下所示:

  • foo.cpp
    翻译单位:

    // Contents of <map>
    // Contents of <string>
    
    class Foo {
    public:
        Foo();
    
    private:
        std::map<std::string, std::string> m;
    };
    
    int main()
    {
        Foo f;
    }
    
    // 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
的定义不满足这些要求,则行为未定义


如您所见,您有未定义的行为。是的,当您使用
声明向量时,它可能会起作用,但这只是偶然的。它仍然有未定义的行为,所以任何事情都可能发生。

您是否意识到
sizeof(Foo)
main.cpp
Foo.cpp
中是不同的?您应该使用适当的
-D
标志编译所有内容。您严重违反了ODR,您期望得到什么?您正在违反语言规则(Plasmah指出的ODR)。试图解释之后发生的事情并不是很有建设性。我知道这是一个bug,正如我上面所说的。但我仍然想知道,为什么这对map来说是失败的,显然,没有别的。据我所知,foo.o中的构造函数/析构函数应该负责分配/解除分配,并且应该可以工作。@fhd不。当你违反规则时,所有手套都会脱掉。当您取消引用空指针时,运行时可能会崩溃或看起来正常工作。你能保证用vector运行代码100000不会崩溃吗?有可能。我不太愿意把这当作莫名其妙的巫毒。Clang和GCC显然表现出了相同的行为,这难道不意味着对此有合理的解释吗?@fhd这不是“无法解释的巫毒”。标准只是说“如果你违反规则,你会有未定义的行为。该标准不会对未定义的行为施加任何要求。”这意味着编译器作者甚至不必关心UB期间发生的事情,因此您得到的通常只是编译器实现细节的随机结果。gcc和clang都是开源的;如果你想知道为什么会这样,你可以自己去寻找。好吧,我放弃了:希望得到一个简单的解释,但我想没有。解释说,“SFLabBIT”解释说“直截了当”的话对于大多数C++用户来说是足够的。
// Contents of <map>
// Contents of <string>

class Foo {
public:
    Foo();

private:
};

int main()
{
    Foo f;
}

int main()
{
    Foo f;
}