jjzjj

c++ - 模板类遇到 __declspec(import) 时出现 Visual Studio 链接器错误

coder 2024-02-23 原文

这始于我在将我的小型异常处理库集成到由单个 Visual Studio 解决方案中的约 200 个 Visual C++ 项目组成的代码库时遇到的一个看似很小的问题。

我有一个链接器问题,由这样的消息表示

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 文件的顺序”或“将实现移出头文件”来解决,但事实并非如此。

我探索了许多类似的相关问题,例如 thisthis one ,但没有一个适合我的情况。至少,建议的食谱对我的问题没有帮助。

此外,很久以前我们公司的人在迁移到VS2010的过程中遇到了另一个与Visual Studio链接器相关的问题,结果是一个链接器错误,see here .无论如何,那个都不符合我的情况。

所以这个小问题最终导致了整个小型研究。您可能会找到重现问题的详细信息和玩具示例 here at github .同时,我会尽可能简要地描述下面的情况。


导致构建失败的关键因素是:

  1. 3 个项目(我们将它们命名为 A、B、C),依赖关系图如下:B->A、C->B、C->A。
  2. 在项目 A 中,我们定义了一个模板类。它在该项目 A 的标题中被实例化。
  3. 项目 B 中的 DLL 导出类继承自 2) 的相同模板类实例。
  4. 项目 C 具有至少两个翻译单元,其中至少一个包括 2) 和 3)。

在代码中,它看起来像这样:

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 .

谁能解释这里出了什么问题? 本质上,我想了解:

  • 我是否违反了这里的某些规则?
  • 还是 Visual Studio 链接器问题?

附言有一种解决方法可以帮助我推进工作。参见 github's README了解详情。但是,我仍然不清楚问题的根本原因。

最佳答案

我仔细研究了您的解决方案,并且逐渐相信它也是一个编译器错误。它可以缩小到一个更小的 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,原始等效项分布在标准库中的多个文件中 - 主要是

如果他们解决了这个问题,请在此处更新。


更新: 报告 3 年后,MS resolved it (rightfully) as not a bug .他们的完整答案被转储在这里,因为过去很多次我都满足于一个链接,但它已经死了。

dllimport/dllexport on a derived class needs to implicitly mark the base class as dllimport/dllexport, too. However, this can introduce ODR (one definition rule) violation as seen in your repro.

In project 'B_Utils', 'ExceptionBase' is implicitly marked as dllimport/dllexport because its derived class 'Error' is marked as dllimport/dllexport.

In project 'C_Client', the same class is not inherited and isn't marked as dllimport/dllexport.

This means when you link the two projects together you end up having two definitions of the same class and this ODR violation causes the linker error because one of its member (the copy consructor) is considered as a real function in 'C_Client' and an imported function in 'B_Utils'.

To avoid the potential ODR violation, it is better to explicitly mark the base class as dllimport/dllexport too.

This means it can be challenging to dllexport a class which derives from a class from the standard library. In this case, you may need to consider using other approaches.

Or you can add this to project 'C_Client' to avoid the ODR violation:

struct _B_UTILS_EXPORTS_CLASS DummyClassToAvoidORDViolation : public ExceptionBase {};

关于c++ - 模板类遇到 __declspec(import) 时出现 Visual Studio 链接器错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45133791/

有关c++ - 模板类遇到 __declspec(import) 时出现 Visual Studio 链接器错误的更多相关文章

  1. ruby - ECONNRESET (Whois::ConnectionError) - 尝试在 Ruby 中查询 Whois 时出错 - 2

    我正在用Ruby编写一个简单的程序来检查域列表是否被占用。基本上它循环遍历列表,并使用以下函数进行检查。require'rubygems'require'whois'defcheck_domain(domain)c=Whois::Client.newc.query("google.com").available?end程序不断出错(即使我在google.com中进行硬编码),并打印以下消息。鉴于该程序非常简单,我已经没有什么想法了-有什么建议吗?/Library/Ruby/Gems/1.8/gems/whois-2.0.2/lib/whois/server/adapters/base.

  2. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  3. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

  4. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  5. ruby - 通过 erb 模板输出 ruby​​ 数组 - 2

    我正在使用puppet为ruby​​程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby​​不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这

  6. ruby - 通过 RVM (OSX Mountain Lion) 安装 Ruby 2.0.0-p247 时遇到问题 - 2

    我的最终目标是安装当前版本的RubyonRails。我在OSXMountainLion上运行。到目前为止,这是我的过程:已安装的RVM$\curl-Lhttps://get.rvm.io|bash-sstable检查已知(我假设已批准)安装$rvmlistknown我看到当前的稳定版本可用[ruby-]2.0.0[-p247]输入命令安装$rvminstall2.0.0-p247注意:我也试过这些安装命令$rvminstallruby-2.0.0-p247$rvminstallruby=2.0.0-p247我很快就无处可去了。结果:$rvminstall2.0.0-p247Search

  7. ruby-on-rails - 迷你测试错误 : "NameError: uninitialized constant" - 2

    我遵循MichaelHartl的“RubyonRails教程:学习Web开发”,并创建了检查用户名和电子邮件长度有效性的测试(名称最多50个字符,电子邮件最多255个字符)。test/helpers/application_helper_test.rb的内容是:require'test_helper'classApplicationHelperTest在运行bundleexecraketest时,所有测试都通过了,但我看到以下消息在最后被标记为错误:ERROR["test_full_title_helper",ApplicationHelperTest,1.820016791]test

  8. ruby-on-rails - 如何在 Rails View 上显示错误消息? - 2

    我是rails的新手,想在form字段上应用验证。myviewsnew.html.erb.....模拟.rbclassSimulation{:in=>1..25,:message=>'Therowmustbebetween1and25'}end模拟Controller.rbclassSimulationsController我想检查模型类中row字段的整数范围,如果不在范围内则返回错误信息。我可以检查上面代码的范围,但无法返回错误消息提前致谢 最佳答案 关键是您使用的是模型表单,一种显示ActiveRecord模型实例属性的表单。c

  9. 使用 ACL 调用 upload_file 时出现 Ruby S3 "Access Denied"错误 - 2

    我正在尝试编写一个将文件上传到AWS并公开该文件的Ruby脚本。我做了以下事情:s3=Aws::S3::Resource.new(credentials:Aws::Credentials.new(KEY,SECRET),region:'us-west-2')obj=s3.bucket('stg-db').object('key')obj.upload_file(filename)这似乎工作正常,除了该文件不是公开可用的,而且我无法获得它的公共(public)URL。但是当我登录到S3时,我可以正常查看我的文件。为了使其公开可用,我将最后一行更改为obj.upload_file(file

  10. ruby-on-rails - 错误 : Error installing pg: ERROR: Failed to build gem native extension - 2

    我克隆了一个rails仓库,我现在正尝试捆绑安装背景:OSXElCapitanruby2.2.3p173(2015-08-18修订版51636)[x86_64-darwin15]rails-v在您的Gemfile中列出的或native可用的任何gem源中找不到gem'pg(>=0)ruby​​'。运行bundleinstall以安装缺少的gem。bundleinstallFetchinggemmetadatafromhttps://rubygems.org/............Fetchingversionmetadatafromhttps://rubygems.org/...Fe

随机推荐