Pour le code C++ suivant, je reçois un comportement inattendu. Le comportement a été vérifiée avec une version récente de GCC, Clang et MSVC++. Pour la déclencher, il est nécessaire de diviser le code entre plusieurs fichiers.
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;
}
Résultat attendu:
foo 1
bar 1
De sortie réelle:
bar 4.94066e-324
bar 1
Ce que j'ai prévu d'arriver:
À l'intérieur de foo.cc une instance de X définie à l'intérieur de foo.cc est et par l'appelant (appel), la mise en œuvre d'un crochet() à l'intérieur foo.cc est appelé. De même pour la barre.
Ce qui se passe réellement:
Une instance de X tel que défini dans foo.cc est faite dans foo(). Mais lors de l'appel téléphonique, il distribue à ne pas accrocher() définie dans foo.cc mais à crochet() définie dans bar.cc. Cela conduit à la corruption, comme l'argument de crochet est toujours un int, pas un double.
Le problème peut être résolu en ajoutant la définition de X dans foo.cc dans un autre espace de noms que de définition de X dans bar.cc
Donc, finalement, à la question: Il n'y a pas de compilateur d'avertissement à ce sujet. Ni gcc, ni clang ou MSVC++ n'a même afficher un avertissement à ce sujet. C'est qu'un comportement valable que définie selon la norme C++?
La situation semble s'être un peu construit, mais il s'est passé dans un scénario réel. J'étais à écrire des essais avec rapidcheck, où les actions possibles sur une unité à tester sont définies comme des classes. La plupart des classes de conteneurs ont des actions similaires, de sorte que lors de l'écriture de tests pour une file d'attente et un vecteur de classes, avec des noms comme "Clair", "Push" ou "Pop" peut venir plusieurs fois. Comme ce sont seulement tenus localement, je les ai mis directement dans les sources où les tests sont exécuter.