Visual C++:为什么调用来自不同编译单元的类方法?

Dam*_*hev 1 c++ visual-studio visual-c++ c++17

我有两个独立的编译单元,没有任何头文件:

\n

单元a.cpp:

\n
#include <algorithm>\n#include <vector>\nclass my_predicate\n{\n    const std::vector<int>& vec;\npublic:\n    my_predicate(const std::vector<int>& container) : vec(container) { }\n\n    bool operator() (size_t idx1, size_t idx2)\n    {\n        return vec[idx1] < vec[idx2];\n    }\n};\n\nint main()\n{\n    std::vector<int> v1,v2;\n    v1.resize(10);\n    v2.resize(10);\n\n    std::sort(v1.begin(), v1.end(), my_predicate(v2));\n}\n
Run Code Online (Sandbox Code Playgroud)\n

单元b.cpp:

\n
#include <algorithm>\n#include <vector>\n\nclass my_predicate\n{\n    const std::vector<char>& vec;\npublic:\n    my_predicate(const std::vector<char>& container) : vec(container) { }\n\n    bool operator() (size_t idx1, size_t idx2)\n    {\n        std::cout << "Why the operator from b.cpp is called?" << std::endl;\n        return vec[idx1] < vec[idx2];\n    }\n};\n\nvoid bar()\n{\n    std::vector<char> v1, v2;\n\n    std::sort(v1.begin(), v1.end(), my_predicate(v2));\n}\n
Run Code Online (Sandbox Code Playgroud)\n

实现上的主要区别my_predicate在于所使用的容器类型:vector<int>vector<char>

\n

正式来说,单元 a.cpp 应该对类一无所知my_predicate

\n

奇怪的事情开始于sort当main from a.cpp 执行

\n
    \n
  1. 它调用正确的构造函数my_predicate它从a.cpp 中
  2. \n
  3. 然后为了排序,突然开始operator()b.cppmy_predicate中调用调用它必须知道的\xe2\x80\x99。
  4. \n
\n

这也会在调试模式下重现,因此此处关闭了整个程序优化。

\n

我是否遗漏了一些东西,现在类定义全局可见,或者这是一个编译器错误?

\n

仅当我有一个虚拟调用时,魔法才会发生my_predicate(请参阅 b.cpp 中的虚拟未使用函数栏)。

\n

作为奖励,如果我尝试重命名my_predicate使用 VS 重命名工具(Ctrl+R、Ctrl+R)在 a.cpp 中重命名,它会在两个文件中重命名它。

\n

Ted*_*gmo 5

bool my_predicate::operator()在两个翻译单元中进行了不同的定义,因此您的链接器可以选择其中任何一个 - 或拒绝重新定义。实际的类定义也不同,因此这是另一个 ODR 违规。

g++

g++ -o x a.o b.o
Run Code Online (Sandbox Code Playgroud)

使其使用 , 中的定义a.o,并且

g++ -o x a.o b.o
Run Code Online (Sandbox Code Playgroud)

使其使用 中的定义b.o

请参阅定义和 ODR(单一定义规则)

为了在实现 ( .cpp) 文件中创建类(和函数等)时不导致这些类型的 ODR 违规,请将它们放入匿名命名空间中。

g++ -o x b.o a.o
Run Code Online (Sandbox Code Playgroud)
// a.cpp
namespace { // anonymous namespace
class my_predicate {
    // a's definition
};
} // anonymous namespace
Run Code Online (Sandbox Code Playgroud)