Для следующего кода C++ я получаю неожиданное поведение. Поведение было проверено с недавними GCC, Clang и MSV C++. Чтобы вызвать его, требуется разделить код на несколько файлов.
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, определенный внутри foo.cc, и через вызов call() вызывается реализация hook() внутри foo.cc. То же самое для бара.
Что на самом деле происходит:
Экземпляр X, определенный в foo.cc, выполнен в foo(). Но при вызове вызова отправляется не на hook(), определенную в foo.cc, а на hook(), определенную в bar.cc. Это приводит к коррупции, поскольку аргумент для привязки по-прежнему является int, а не двойным.
Проблема может быть решена путем размещения определения X внутри foo.cc в другом пространстве имен, кроме определения X внутри bar.cc
Итак, наконец, вопрос: об этом нет предупреждений компилятора. Ни gcc, ни clang, ни MSV C++ даже не предупредили об этом. Является ли это поведение действительным, как определено в стандарте C++?
Ситуация, похоже, немного сконструирована, но это произошло в реальном мире. Я писал тесты с быстрым чеком, где возможные действия над тестируемым модулем определяются как классы. Большинство классов контейнеров имеют схожие действия, поэтому при написании тестов для очереди и вектора классы с именами типа "Clear", "Push" или "Pop" могут появляться несколько раз. Поскольку они требуются только локально, я помещаю их непосредственно в источники, где выполняются тесты.