令人高兴地连接不兼容的类型会导致混乱

bit*_*ask 4 c++ linker compiler-bug

我一直试图找出一些边界g++,特别是链接(C++)目标文件.我发现以下好奇心,我试着尽可能多地压缩,然后再询问.

文件 common.h

#ifndef _COMMON_H
#define _COMMON_H

#include <iostream>

#define TMPL_Y(name,T) \
struct Y { \
  T y; \
  void f() { \
    std::cout << name << "::f " << y << std::endl; \
  } \
  virtual void vf() { \
    std::cout << name << "::vf " << y << std::endl; \
  } \
  Y() { \
    std::cout << name << " ctor" << std::endl; \
  } \
  ~Y() { \
    std::cout << name << " dtor" << std::endl; \
  } \
}

#define TMPL_Z(Z) \
struct Z { \
  Y* y; \
  Z(); \
  void g(); \
}

#define TMPL_Z_impl(name,Z) \
Z::Z() { \
  y = new Y(); \
  y->y = name; \
  std::cout << #Z << "(); sizeof(Y) = " << sizeof(Y) << std::endl; \
} \
void Z::g() { \
  y->f(); \
  y->vf(); \
}

#endif
Run Code Online (Sandbox Code Playgroud)

文件a.cpp编译用g++ -Wall -c a.cpp

#include "common.h"

TMPL_Y('a',char);

TMPL_Z(Za);

TMPL_Z_impl('a',Za);
Run Code Online (Sandbox Code Playgroud)

文件b.cpp编译用g++ -Wall -c b.cpp

#include "common.h"

TMPL_Y('b',unsigned long long);

TMPL_Z(Zb);

TMPL_Z_impl('b',Zb);
Run Code Online (Sandbox Code Playgroud)

文件main.cpp编译和链接g++ -Wall a.o b.o main.cpp

#include "common.h"

struct Y;
TMPL_Z(Za);
TMPL_Z(Zb);

int main() {
  Za za;
  Zb zb;
  za.g();
  zb.g();
  za.y = zb.y;
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

结果./a.out

a ctor
Za(); sizeof(Y) = 8
a ctor  // <- mayhem
Zb(); sizeof(Y) = 12
a::f a
a::vf a
a::f b  // <- mayhem
a::vf b // <- mayhem
Run Code Online (Sandbox Code Playgroud)

现在,我本来预计g++给我打电话一些讨厌的名字试图链接a.ob.o在一起.特别是任命za.y = zb.y是邪恶的.这不仅g++没有抱怨,我希望它将具有相同名称(Y)的不兼容类型链接在一起,但它完全忽略b.o(resp.b.cpp)中的辅助定义.

我的意思是我没有做一些遥远的事情.两个编译单元可以对本地类使用相同的名称是非常合理的,尤其是 在一个大型项目中.

这是一个错误吗?有人能解释一下这个问题吗?

gha*_*.st 5

引用Bjarne Stroustrup的"The C++ Programming Language":

9.2联系

必须在所有翻译单元中一致地使用函数,类,模板,变量,名称空间,枚举和枚举器的名称,除非它们明确指定为本地.

程序员的任务是确保每个名称空间,类,函数等在其出现的每个翻译单元中正确声明,并且所有引用同一实体的声明都是一致的.[...]