[英]C++ class name collision
对于以下C ++代码,我遇到了意外行为。 最近的GCC,Clang和MSVC ++证实了这种行为。 要触发它,需要在几个文件之间拆分代码。
def.h
#pragma once
template<typename T>
struct Base
{
void call() {hook(data);}
virtual void hook(T& arg)=0;
T data;
};
foo.h中
#pragma once
void foo();
foo.cc
#include "foo.h"
#include <iostream>
#include "def.h"
struct X : Base<int>
{
virtual void hook(int& arg) {std::cout << "foo " << arg << std::endl;}
};
void foo()
{
X x;
x.data=1;
x.call();
}
bar.h
#pragma once
void bar();
bar.cc
#include "bar.h"
#include <iostream>
#include "def.h"
struct X : Base<double>
{
virtual void hook(double& arg) {std::cout << "bar " << arg << std::endl;}
};
void bar()
{
X x;
x.data=1;
x.call();
}
main.cc
#include "foo.h"
#include "bar.h"
int main()
{
foo();
bar();
return 0;
}
预期产量:
foo 1
bar 1
实际产量:
bar 4.94066e-324
bar 1
我期望发生的事情:
在foo.cc中,在foo.cc中定义了一个X实例,并通过调用call(),调用foo.cc中的hook()实现。 酒吧也一样。
实际发生了什么:
foo.cc中定义的X实例是在foo()中创建的。 但是当调用call时,它不会调度到foo.cc中定义的hook(),而是发送到bar.cc中定义的hook()。 这会导致损坏,因为hook的参数仍然是int,而不是double。
通过将f的定义放在foo.cc中的另一个命名空间而不是bar.cc中的X定义,可以解决这个问题
最后问题是:没有关于此的编译器警告。 gcc,clang或MSVC ++都没有对此发出警告。 该行为是否按照C ++标准定义有效?
情况似乎有点构建,但它发生在现实世界的场景中。 我正在使用rapidcheck编写测试,其中对要测试的单元的可能操作被定义为类。 大多数容器类都有类似的操作,因此在为队列和向量编写测试时,名称如“Clear”,“Push”或“Pop”的类可能会多次出现。 由于这些仅在本地需要,我将它们直接放在执行测试的源中。
在不同的编译单元中,有两个相同名称但不同的类X
,导致程序格式错误,因为现在有两个具有相同名称的符号。 由于只能在链接期间检测到问题,因此编译器无法(并且不需要)报告此问题。
避免此类事情的唯一方法是将任何 不打算导出的代码(特别是所有尚未在头文件中声明的代码)放入匿名或未命名的命名空间中 :
#include "foo.h"
#include <iostream>
#include "def.h"
namespace {
struct X : Base<int>
{
virtual void hook(int& arg) {std::cout << "foo " << arg << std::endl;}
};
}
void foo()
{
X x;
x.data=1;
x.call();
}
并且相当于bar.cc
实际上,这是未命名命名空间的主要(唯一?)目的。
简单地重新命名您的类(例如fooX
和barX
)可能在实践中对您barX
,但不是一个稳定的解决方案,因为无法保证这些符号名称不被链接加载的某些模糊的第三方库使用 - 或运行时(现在或将来的某个时间点)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.