代码之家  ›  专栏  ›  技术社区  ›  Damir Tenishev

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

  •  0
  • Damir Tenishev  · 技术社区  · 2 年前

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

    单位a.cp:

    #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));
    }
    

    单位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));
    }
    

    关键区别在于 my_predicate 实现是所使用的容器类型: vector<int> vector<char>

    正式的单元a.pp应该对课堂一无所知 我重新定位(_P) 来自b.cpp单元。

    奇怪的事情开始于 sort 主要执行来自a.cpp的:

    1. 它从调用正确的构造函数 我重新定位(_P) 在a.cpp中
    2. 然后为了排序,突然开始呼叫 operator() 从…起 我重新定位(_P) 在里面 捕获信号 它一定不知道。

    这也是在调试模式下复制的,所以这里关闭了整个程序优化。

    是我遗漏了什么,现在类定义全局可见,还是这是编译器错误?

    只有当我对 我重新定位(_P) 在b.cpp中(请参见b.cpp的伪未使用函数栏)。

    作为奖励,如果我尝试重命名 我重新定位(_P) 在带有VS重命名工具(Ctrl+R,Ctrl+R)的.cpp中,它可以在两个文件中对其进行重命名。

    1 回复  |  直到 2 年前
        1
  •  5
  •   Ted Lyngmo    2 年前

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

    在里面 g++

    g++ -o x a.o b.o
    

    使其使用中的定义 a.o

    g++ -o x b.o a.o
    

    使其使用中的定义 b.o

    看见 Definitions and ODR (One Definition Rule)

    当您在实现中创建类(和函数等)时,为了不引起这些类型的ODR冲突( .cpp )文件,将它们放在匿名命名空间中。

    // 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