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内部,定义了一个X的实例,并通过调用call()来调用foo.cc内的hook()的实现。bar也是同样的情况。
实际发生的情况:
在foo()中定义了一个在foo.cc中定义的X的实例。但是,在调用call时,它并不会分派到foo.cc中定义的hook(),而是分派到bar.cc中定义的hook()。这导致出现损坏,因为hook的参数仍然是int,而不是double。
问题可以通过将foo.cc中X的定义放在与bar.cc中X的定义不同的命名空间中来解决。
最终的问题是:没有编译器警告。无论是gcc、clang还是MSVC++都没有显示任何关于此的警告。按照C++标准的定义,这种行为是否有效?
这种情况似乎有点牵强,但它确实发生在真实世界的场景中。我正在使用rapidcheck编写测试,其中对要测试的单元的可能操作被定义为类。大多数容器类都具有相似的操作,因此当为队列和向量编写测试时,类似“Clear”、“Push”或“Pop”的名称的类可能会出现多次。由于这些只在本地需要,所以我直接将它们放在执行测试的源文件中。
X
移动到匿名命名空间中(这将为它们提供一个唯一但隐藏的名称)。 - Acorn