Duplicate symbol --- 定义和声明的区别

Duplicate symbol --- Difference between definition and declaration

问题

我是 C++ 的新手,我正在做一个在控制台中绘制各种分布的家庭作业问题,但我的项目无法编译。当我将所有内容都放在同一个文件中时,一切都运行良好且花花公子,但我的教授希望我们将其拆分为一个 hpp 文件、一个 cpp 文件和一个主文件。我只是将所有内容都移了过来,现在由于重复符号错误而无法编译。

定义与声明

我知道您只想在头文件中包含函数声明,因此我添加了这些声明,但在 cpp 文件中保留了函数的实际定义。我在包含头文件的 cpp 文件中有一个 include 语句,在 main.cpp 中有另一个包含 cpp 文件的语句。
我知道我的问题可能与我的 include 语句有关,但我不知道如何解决它。

distributions.hpp

这个文件是class我写的四个函数的定义和声明

#include <iostream>

class DistributionPair
{
  public:
    DistributionPair(std::uint32_t minValue, std::uint32_t maxValue) :
        minValue(minValue),
        maxValue(maxValue),
        count(0)
    {
    }

    std::uint32_t minValue;
    std::uint32_t maxValue;
    std::uint32_t count;
};

std::vector<DistributionPair> generateUniformDistribution(std::uint32_t, std::uint32_t, std::uint32_t, std::uint8_t);
std::vector<DistributionPair> generateNormalDistribution(std::uint32_t, float, float, std::uint8_t);
std::vector<DistributionPair> generatePoissonDistribution(std::uint32_t, std::uint8_t, std::uint8_t);
void plotDistribution(std::string, const std::vector<DistributionPair>&, const std::uint8_t);

distributions.cpp

此文件包含这四个函数的其余定义

#include <iostream>
#include <iomanip>
#include <random>
#include "distributions.hpp"

std::vector<DistributionPair> generateUniformDistribution(std::uint32_t howMany, std::uint32_t min, std::uint32_t max, std::uint8_t numBins)
{......

main.cpp

这是绘制分布的主要函数

#include <iostream>
#include "distributions.cpp"

int main()
{......

错误信息

以下是我在编译代码时收到的错误消息:

duplicate symbol 'generateUniformDistribution(unsigned int, unsigned int, unsigned int, unsigned char)' in:
    /Users/ryanegbert/Desktop/c++/assign2/build/RandDistributions.build/Debug/RandDistributions.build/Objects-normal/x86_64/distributions.o
    /Users/ryanegbert/Desktop/c++/assign2/build/RandDistributions.build/Debug/RandDistributions.build/Objects-normal/x86_64/main.o
..........

ld: 4 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

如果我使用 #include "distributions.hpp" 那么我会得到一个不同的编译错误:

/Users/ryanegbert/Desktop/c++/assign2/main.cpp:6:20: error: implicit instantiation of undefined template 'std::__1::vector<DistributionPair, std::__1::allocator<DistributionPair> >'
    auto uniform = generateUniformDistribution(100000, 0, 79, 40);

因为我的函数是在 distributions.cpp 中定义的。

我在 Mac 上使用 Xcode v11.0。

有人能帮我解释为什么我会遇到这个编译错误吗?

请注意这是一个链接器错误,而不是编译器错误。

将可执行文件构建视为一个两阶段过程。

  • 在第一阶段,编译器读取源代码并生成 .o 文件。
  • 在第二阶段,链接器读取.o(和其他)文件并生成可执行文件。

.o 文件同时包含二进制代码和符号引用。编译器生成两种东西:函数 已定义 的二进制代码,以及函数 已声明但未定义 的符号引用。链接器的任务是解决所有引用,即将所有符号引用与二进制代码相关联,构建一个完整的可执行文件。

您的示例中似乎发生的是您的构建脚本正在执行以下操作:

  • 编译'main.cpp'+'distributions.cpp'(包含)成'main.o'
  • 将'distributions.cpp'编译成'distributions.o'
  • Link一起'main.o'+'distributions.o'进入最终的可执行文件

这样,链接器就有两份来自 distributions.cpp 的编译代码副本,一份在 main.o 中,另一份在 distributions.o

要解决它,您必须执行以下操作之一:

  • 指示链接器只使用'main.o'创建可执行文件

或者,

  • #include "distributions.cpp" 替换为 #include "distributions.hpp" 以便您的 main.o 文件将只包含符号引用,链接器会将 distributions.o 中的符号和代码粘合到可执行文件中。

您必须在 main.cpp 中 #include "distributions.hpp" 而不是 #include "distributions.cpp",但您还必须在开始时包括函数原型所依赖的所有 headers if "distributions.hpp":

#include <vector>
#include <cstdint>