C++中头文件出现问题:链接器命令失败

4

提前道歉,我相信这是一个非常简单的问题,但我一直无法找到解决方法。我正在组建一个非常简单的 C++ 项目,但在适当使用头文件方面遇到了麻烦。

目录结构如下:

.
├── mainGraph
│   └── hnsw.cpp
└── utils
    ├── distances.cpp
    └── distances.h

距离头文件 (distances.h):
#ifndef DISTANCES
#define DISTANCES

#include <vector>

enum class Metric { Cosine, Euclidean};

double distance(const std::vector<double>& A, const std::vector<double>& B, Metric metric);
double similarity(const std::vector<double>& A, const std::vector<double>& B, Metric metric);

#endif

最后是 hnsw.cpp 文件:
#include "../utils/distances.h"
#include <vector>
#include <iostream>

int main(){
    std::vector<double> A = {0.1, 0.5, 0.7, 1.1};
    std::vector<double> B = {0.5, 0.3, 0.8, 0.9};

    std::cout << distance(A, B, Metric::Cosine) << '\n';
}

现在,当我尝试使用以下命令编译hnsw.cpp时:g++ hnsw.cpp --std=c++11,我会得到以下错误:

Undefined symbols for architecture x86_64:
  "distance(std::__1::vector<double, std::__1::allocator<double> > const&, std::__1::vector<double, std::__1::allocator<double> > const&, Metric)", referenced from:
      _main in hnsw-ad0e05.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

如果我将主函数移动到distances.cpp文件中,则一切都可以正常工作。为了简洁起见,我不会发布整个文件,因为它很大,而且我相当确定它并不相关。如果有任何关于理解此处的一般流程的资源,那也会非常有帮助。

2个回答

5
如果您不想为以后的链接构建单独的对象文件,而是一次性构建整个可执行文件,您需要在命令行上指定所有参与的翻译单元:
g++ -std=c++11 hnsw.cpp ../utils/distances.cpp

输出文件将被命名为a.out,除非您还使用-o标志覆盖该名称。
通常情况下,您会单独编译翻译单元并在单独的步骤中链接它们:
g++ -std=c++11 mainGraph/hnsw.cpp -c -o mainGraph/hnsw.o
g++ -std=c++11 utils/distances.cpp -c -o utils/distances.o

g++ -std=c++11 mainGraph.hnsw.o utils/distances.o -o myprog

你也不需要手动维护这些命令,而是将它们放入Makefile或类似的构建系统中。重点是,当你修改源代码的一部分时,你不需要重新编译那些没有改变的翻译单元。


这非常有帮助。回想起来似乎很明显,但并非所有事情都是如此。我最后一个问题只是关于你第二部分的顺序。你为什么要先编译hnsw而不是distances?看起来反过来更有意义,因为hnsw需要distances。 - Slater Victoroff
@SlaterTyranus:这两个并不相关,可以按任意顺序编译,或者实际上并行编译(特别是如果你有多个核心)。这正是make将为您免费完成的。 - Kerrek SB
@SlaterTyranus:需要明确的是,在任何有意义的意义上,这两个翻译单元都不“需要”彼此。只有链接需要所有的翻译单元。(但是,这两个翻译单元都依赖于文件distances.h,所以如果更改该文件,则需要重新构建两个TU。该文件是两个TU的一部分。) - Kerrek SB
完全有道理。感谢下载。你知道一些好的资源可以让我更好地了解整个过程的后端吗? - Slater Victoroff

3
当您运行g++ hnsw.cpp --std=c++11时,您正在告诉编译器编译hnsw.cpp并将输出与c ++标准库链接以创建可执行文件。 链接器无法找到distances的实现,因为它位于distances.cpp中。
您需要编译这两个文件并将它们链接在一起。有许多方法可以实现此目的,但最简单的方法是在调用gcc时指定两个源文件,如下所示:
g++ -std=c++11 hnsw.cpp ../utils/distances.cpp

谢谢你的回答!很遗憾你在FGIW战斗中输给了那个拥有305k声望值的人 :) - Slater Victoroff

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接