1

我希望有一些包装代码是一个只有头文件的库。我受到了 boost 库的启发,将它们保留为标题只是为了简化分发 .lib 和包括编译 .cpp 的需要。

在此示例中,“zh”是包装器,a.cp​​p 已被重构以将 B() 移动到它自己的源文件中。现在它不起作用。

zh

class Z
{
    public:
    void Foo(); // edited to match my code
};

Z::Foo() { } 

a.cpp

#include "z.h"
void A() { 
      Z z;
      z.Foo();
}
//void B(Z z) {
//     z.Foo();
//}

b.cpp

#include "z.h"
void B(Z z) {           
      z.Foo();
}

*error LNK2005: "public: __cdecl void z::Foo()" 已经在 b.obj 中定义*

我知道我可以通过将 zh 分离为 zh 用于声明和 z.cpp 用于定义来解决此问题。

  • 但是,没有 .cpp 文件,Boost 库如何摆脱困境呢?
  • 一切都需要模板吗?
  • zh里能进去什么代码?
4

1 回答 1

2

考虑一下这个标题:

// foo.hpp

void foo(int x)
{
    /* do something */
}

(标题保护在这里无关紧要,它们按翻译单元工作。)我们现在有两个翻译单元:

// a.cpp

#include "foo.hpp"

和:

// b.cpp

#include "foo.hpp"

最终要做的是定义foo(int x)两次,每个 TU 中定义一次。根据单一定义规则 (ODR),不允许多个定义,虽然从技术上讲不需要诊断,但在编译器部分这样做是微不足道的,因此您会得到错误。

幸运的是,有一个关键字 ,inline可以改变这种行为:

// foo.hpp

inline void foo(int x)
{
    /* do something */
}

这个关键字告诉链接器,如果它遇到多个定义,它可以自由地选择一个定义并丢弃其余的。(由您来确保这实际上没问题!)通过此更改,先前的 ODR 违规得到解决并且程序编译。

现在,您已将标题列为:

class Z      
{      
    public:      
    void Foo(){       
       //do stuff      
    }      
};     

这相当于:

class Z      
{      
    public:      
    void Foo();
};    

inline void Z::Foo(){       
       //do stuff      
    }

因为在类中定义的函数是隐式的inline。(这使您可以将定义包含在多个翻译单元中而不会出错。)我怀疑您所写的不是您帖子中的内容,而是如下所示:

class Z      
{      
    public:      
    void Foo();
};    

void Z::Foo(){       
       //do stuff      
    }

哪里inline不见了。等价性丢失,创建了多个定义,并且您违反了 ODR。

解决方案是使用inline,或通过定义隐式使用它类定义中的函数。就个人而言,我发现后者更清晰(因为您避免重复自己),并且更容易维护。Boostinline经常使用来避免需要单个定义。

于 2012-08-15T23:15:34.870 回答