我正在为神经网写一个库。我需要一些必要的功能,因此我将它们分隔在单独的标头文件中。我还提供了定义后卫。我还将标题文件仅在一个文件中包括在一个文件中,但随后链接器声称程序中所有功能都有多个定义。库结构如下:
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
您必须在.hpp或.h文件以外的.cpp文件中编写random_double()
的代码。或者,如果将代码保存在.hpp文件中,请在double random_double() { //some code }
之前添加inline
。
问题
您的功能定义具有其完整代码,并在标题中包含在几个汇编单元中。这会导致该函数在每个汇编单元(CPP)中定义,这打破了一个定义规则(ODR)。
include guards确保在同一汇编单元中不会发生相同的定义几次(例如,如果将function.hpp
包括在neuron.hpp
中,并且也直接包含它)。但是在这里,此标头直接或间接包含在main.cpp
,ffnet.cpp
和neuron.cpp
中,该标题是第一个定义和2个无效的重新定义。
解决方案
您必须更改function.hpp
才能仅保留函数声明:
#ifndef FUNCTIONS_HPP
#define FUNCTIONS_HPP
double random_double(); // no body !!
#endif
并将功能体移至单独的function.cpp
,必须添加到您的编译器命令中。
这种方法的优点是:
- 然后,您可以单独编译实用程序功能。每次更改功能主体时,您都不必再重新编译所有CPP。
- 通过仅在HPP中共享其他模块需要知道的内容,并隐藏实现细节。 ,可以改善封装。
- 可以通过制作功能库来促进重复使用。
- 其中的内容将较短(在某个遥远的将来,您的代码将演变为具有数千个HPP的大型项目,这可能会使您获得一些时间)
其他备注
不确定它是否适用,但也请注意,将标头纳入名称空间不是一个好主意。
我还建议阅读有关标题的这篇文章。它很旧,但建议仍然非常相关: - )
请注意,类和内联函数的ODR有例外,在这种情况下,多个定义必须是代币的完全相同的序列。