C++中同一函数的多个定义

Multiple definitions of same function in C++

我正在为神经网络编写一个库。我需要一些必要的功能,所以我将它们放在一个单独的头文件中。我还提供了守卫的定义。我还将头文件仅包含在一个文件中,但链接器还声称程序中的所有函数都有多个定义。 文库结构如下:

namespace maya:
     class neuron [neuron.hpp, neuron.cpp]
     class ffnet [ffnet.hpp, ffnet.cpp]
     struct connection [connection.hpp]
     functions [functions.hpp]

函数头文件是这样写的:

#ifndef FUNCTIONS_HPP
#define FUNCTIONS_HPP
// some functions here
double random_double(){//some code}
#endif

这个 functions.hpp 文件只包含在 neuron.hpp 中,因为 ffnet 依赖于神经元,所以我只在 ffnet 中包含 neuron.hpp 一次。此 ffnet.hpp 仅包含在 main.cpp 中一次。 main.cpp 是我用来测试我的库的文件。

此链接器抛出如下错误:
/usr/bin/ld: /tmp/ccN7ywby.o: in function `maya::random_double()': neuron.cpp:(.text+0x0): multiple definition of maya::random_double()'; /tmp/ccvDr1aG.o:main.cpp:(.text+0x0): first defined here

/usr/bin/ld: /tmp/cc66mBIr.o: in function `maya::random_double()':`` ffnet.cpp:(.text+0x0): multiple definition of `maya::random_double()'; /tmp/ccvDr1aG.o:main.cpp:(.text+0x0): first defined here

我还使用 :
编译了我的程序 g++ main.cpp neuron.cpp ffnet.cpp -o net

我认为不需要这样做,但以防万一:
$ uname -a
Linux brightprogrammer 4.19.0-kali3-amd64 #1 SMP Debian 4.19.20-1kali1 (2019-02-14) x86_64 GNU/Linux

问题

您的函数定义及其完整代码在 header 中,您将其包含在多个编译单元中。这会导致函数在每个 编译单元 (cpp) 中定义 ,这会破坏 One Definition Rule (ODR)

包含保护确保相同的定义不会在同一个编译单元中多次出现(例如,如果您在 neuron.hpp 中包含 function.hpp 并且还直接包含它)。但是这里这个header直接或间接包含在main.cppffnet.cppneuron.cpp中,造成了第一次定义和2次无效重定义。

解决方案

您必须更改 function.hpp 以仅保留函数声明:

#ifndef FUNCTIONS_HPP
#define FUNCTIONS_HPP
double random_double();  // no body !!
#endif

并将函数体移动到单独的 function.cpp,必须将其添加到您的编译器命令中。

这种方法的优点是:

  • 然后您可以单独编译实用程序函数。每次更改函数 body 时,您将不再需要重新编译所有 cpp。
  • 通过仅在 hpp 中共享其他模块需要知道的内容并隐藏实现细节,改进了封装。
  • 可以通过创建一个函数库来促进跨项目的重用。
  • includes 会更短(如果在遥远的将来您的代码会演化为具有数千 hpp 的大型项目,这可能会让您获得一些时间)

补充说明

不确定它是否适用,但也要注意将 header 包含到命名空间中并不是一个好主意。

我也推荐reading this article about headers。它很旧,但建议仍然很相关:-)

请注意,类 和内联函数的 ODR 存在例外情况,在这种情况下,多个定义必须是完全相同的标记序列。

您必须在 .cpp 文件而不是 .hpp 或 .h 文件中编写 random_double() 的代码。或者,如果您将代码保存在 .hpp 文件中,请在 double random_double() { //some code } 之前添加 inline