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

bq3bfh9z  于 2023-10-23  发布在  其他
关注(0)|答案(1)|浏览(129)

我有两个独立的编译单元,没有任何头文件:
单位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));
}

单位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.cpp应该不知道来自单元b.cpp的类my_predicate
奇怪的事情开始于sort in main from a.cpp被执行:
1.它从a.cpp中的my_predicate调用正确的构造函数
1.然后为了排序,突然开始从B.cpp中的my_predicate调用operator(),这是它不知道的。
这也是在优化模式下重现的,所以在这里关闭了整个程序优化。
我错过了什么,现在类定义全局可见,或者这是一个编译器错误?
只有当我在b.cpp中对my_predicate有一个伪调用时,魔法才会发生(参见b.cpp中的伪未使用函数栏)。
作为奖励,如果我尝试在a.cpp中使用VS重命名工具(Ctrl+R,Ctrl+R)重命名my_predicate,它会在两个文件中重命名它。

c86crjj0

c86crjj01#

您在两个翻译单元中以不同的方式定义了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)
为了在实现(.cpp)文件中创建类(和函数等)时不导致这些类型的ODR违规,请将它们放在匿名名称空间中。

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

相关问题