这是从一个看似很小的问题开始的,当我将一个小的异常处理库集成到一个由单个Visual Studio解决方案中的~200个Visual C ++项目组成的代码库中时,我遇到了这个问题。
我有一个像这样的消息表达的链接器问题</ p>
3>B_Utils.lib(B_Utils.dll) : error LNK2005: "public: __cdecl ExceptionBase<class std::runtime_error>::ExceptionBase<class std::runtime_error>(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (??0?$ExceptionBase@Vruntime_error@std@@@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) already defined in TranslationUnit_2.obj
3>B_Utils.lib(B_Utils.dll) : error LNK2005: "public: virtual __cdecl ExceptionBase<class std::runtime_error>::~ExceptionBase<class std::runtime_error>(void)" (??1?$ExceptionBase@Vruntime_error@std@@@@UEAA@XZ) already defined in TranslationUnit_2.obj
3>B_Utils.lib(B_Utils.dll) : error LNK2005: "public: __cdecl ExceptionBase<class std::runtime_error>::ExceptionBase<class std::runtime_error>(class ExceptionBase<class std::runtime_error> const &)" (??0?$ExceptionBase@Vruntime_error@std@@@@QEAA@AEBV0@@Z) already defined in TranslationUnit_2.obj
乍一看,它看起来是另一个典型的问题,可以通过典型的建议“尝试更改#include文件的顺序”或“将实现移出头文件”来解决,但事实并非如此。 / p>
我已经探讨过许多类似的相关问题,例如this或this one,但这些问题都不适合我的情况。至少,提议的食谱对我的问题没有帮助。
此外,很久以前我们公司的人员在迁移到VS2010期间遇到了与Visual Studio链接器相关的另一个问题,结果证明是链接器错误see here。无论如何,这个都不符合我的情景。
所以这个小小的问题最终得到了一个小小的研究。您可以找到重现问题的详细信息和玩具示例here at github。与此同时,我将尝试尽可能简短地描述下面的情况。
导致构建失败的关键因素是:
在代码中,它看起来像这样:
A_SDK :
(ExceptionBase.h)
template<typename T>
class ExceptionBase;
--
(foo.h)
#include "ExceptionBase.h"
inline void foo() // same effect would be with template<typename T> void foo()
{
throw ExceptionBase<std::runtime_error>("Problem");
}
B_Utils :
(Error.h)
#include "ExceptionBase.h"
#if defined(B_EXPORTS)
#define _B_UTILS_EXPORTS_CLASS __declspec(dllexport)
#else
#define _B_UTILS_EXPORTS_CLASS __declspec(dllimport)
#endif
struct _B_UTILS_EXPORTS_CLASS Error : public ExceptionBase<std::runtime_error>
{ Error(std::string&& s); } // ctor definition is in *.cpp file
C_Client :
(TranslationUnit_1.cpp)
#include "A_SDK/foo.h"
#include "B_Utils/Error.h" // !!! Comment this line --> Build succeeds
void TranslationUnit_1() {
foo(); // !!! Comment this line --> Build succeeds
}
(TranslationUnit_2.cpp)
#include "A_SDK/foo.h"
void TranslationUnit_2() {
foo(); // !!! Comment this line --> Build succeeds
}
注意那些标有// !!!
的行。评论任何将使构建成功。如上所述,可以获得完整的来源at github。
有人能解释这里出了什么问题吗? 基本上,我想了解:
P.S。有一种解决方法可以帮助我推进我的工作。有关详细信息,请参阅github's README。但是,问题的根本原因对我来说仍然不清楚。
答案 0 :(得分:1)
Visual Studio支持团队对这个问题进行了分类,结果他们提交了一个错误。正在进行调查,详情可见on Visual Studio forum。 一旦我得到他们的进一步反馈,我就会更新这个答案。
PS :这是否会被识别为链接器错误,我想说明从DLL中导出复杂的C ++接口并不是一个好的做法。一般。不幸的是,我无法在我正在使用的代码库中更改它。
就个人而言,对于基于DLL插件的架构,我建议使用一些编组层(如COM)或根据&#34; Hourglass原则&#34;明确设计接口。这是a great talk from CppCon 2014,它解释了那是什么。
答案 1 :(得分:1)
我在你的解决方案上挖了一下,并且逐渐相信它是一个编译器错误。它可以缩小到一个小得多的repro,只有2个二进制文件和零标准库依赖项 - 你可以检查它here。
似乎引起混乱的关键代码就是这个 -
struct BaseWithVirtual
{
virtual void what() {} // !!! make this non-virtual --> Build succeeds
};
template<typename dummy>
struct ExceptionBase : public BaseWithVirtual
{
};
struct ExceptionChild : public ExceptionBase<int>
{
};
inline void ThrowChild()
{
ExceptionChild up;
throw up; // !!! comment this --> Build succeeds
}
这是Dropbox链接中的ThrowChild.h,原始的等价物分布在标准库中的多个文件中 - 主要是&lt;例外&GT;
如果他们解决此问题,请在此处更新。