Tenho duas unidades de compilação separadas sem arquivos de cabeçalho:
Unidade a.cpp:
#include <algorithm>
#include <vector>
class my_predicate
{
const std::vector<int>& vec;
public:
my_predicate(const std::vector<int>& container) : vec(container) { }
bool operator() (size_t idx1, size_t idx2)
{
return vec[idx1] < vec[idx2];
}
};
int main()
{
std::vector<int> v1,v2;
v1.resize(10);
v2.resize(10);
std::sort(v1.begin(), v1.end(), my_predicate(v2));
}
Unidade b.cpp:
#include <algorithm>
#include <vector>
class my_predicate
{
const std::vector<char>& vec;
public:
my_predicate(const std::vector<char>& container) : vec(container) { }
bool operator() (size_t idx1, size_t idx2)
{
std::cout << "Why the operator from b.cpp is called?" << std::endl;
return vec[idx1] < vec[idx2];
}
};
void bar()
{
std::vector<char> v1, v2;
std::sort(v1.begin(), v1.end(), my_predicate(v2));
}
A principal diferença na my_predicate
implementação é o tipo de contêiner usado: vector<int>
e vector<char>
.
Formalmente, a unidade a.cpp não deve saber nada sobre a classe my_predicate
da unidade b.cpp.
Coisas estranhas começam quando sort
in main from a.cpp é executado:
- Ele chama o construtor correto
my_predicate
em a.cpp - E então , para classificação, de repente começa a ligar
operator()
demy_predicate
b.cpp que ele não deve saber.
Isso também é reproduzido no modo de depuração, portanto, a otimização de todo o programa está desativada aqui.
Estou faltando alguma coisa e agora as definições de classe são visíveis globalmente ou isso é um bug do compilador?
A mágica acontece apenas se eu tiver uma chamada fictícia my_predicate
em b.cpp (consulte a barra de funções fictícias não utilizadas em b.cpp).
Como bônus, se eu tentar renomear my_predicate
em a.cpp com a ferramenta VS Rename (Ctrl+R,Ctrl+R), ele oferece renomeá-lo em ambos os arquivos.
Você definiu
bool my_predicate::operator()
de forma diferente em duas unidades de tradução e, portanto, seu vinculador poderia escolher qualquer uma delas - ou recusar a redefinição. As definições reais de classe também são diferentes, então essa é outra violação do ODR.Em
g++
faz com que use a definição em
a.o
, efaz com que ele use a definição em
b.o
.Consulte Definições e ODR (Regra de Uma Definição) .
Para não causar esses tipos de violações de ODR ao criar classes (e funções, etc.) em seus
.cpp
arquivos de implementação ( ), coloque-os em namespaces anônimos.