I have two separate compilation units without any header files:
Unit 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));
}
Unit 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));
}
The key difference in my_predicate implementation is the type of container used: vector<int> and vector<char>.
Formally unit a.cpp should know nothing about class my_predicate from unit b.cpp.
Weird things starts when sort in main from a.cpp is executed:
- It calls the correct constructor from
my_predicatein a.cpp - And then for sorting, suddenly starts to call
operator()frommy_predicatein b.cpp which it mustn’t know.
This is reproduced in Debug mode as well, so Whole Program Optimization is turned off here.
Am I missing something and now class definitions globally visible or this is a compiler bug?
Magic happens only if I have a dummy call to my_predicate in b.cpp (see dummy unused function bar in b.cpp).
As a bonus, if I try to rename my_predicate in a.cpp with VS Rename tool (Ctrl+R,Ctrl+R), it offers to rename it in both files.
>Solution :
You’ve defined bool my_predicate::operator() differently in two translation units and your linker therefore could pick any of them – or refuse the redefinition. The actual class definitions are different too, so that’s another ODR violation.
In g++
g++ -o x a.o b.o
makes it use the definition in a.o, and
g++ -o x b.o a.o
makes it use the definition in b.o.
See Definitions and ODR (One Definition Rule).
To not cause these types of ODR violations when you create classes (and functions etc) in your implementation (.cpp) files, put them in anonymous namespaces.
// a.cpp
namespace { // anonymous namespace
class my_predicate {
// a's definition
};
} // anonymous namespace
// b.cpp
namespace { // anonymous namespace
class my_predicate {
// b's definition
};
} // anonymous namespace