Visual Studio 2010的奇怪“warning LNK4042”

84

我刚刚在Visual Studio 2010 (C++)上遇到了一些严重的警告,让我受到了打击。

编译出现了以下输出:

1 Debug\is.obj : 警告 LNK4042: 对象被指定多次;忽略额外内容
1 Debug\make.obj : 警告 LNK4042: 对象被指定多次;忽略额外内容
1 Debug\view.obj : 警告 LNK4042: 对象被指定多次;忽略额外内容
1 identity.obj : 错误 LNK2019: 未解析的外部符号 void __cdecl test::identity::view(void) (?view@identity@test@@YAXXZ),该符号在函数 void __cdecl test::identity::identity(void) (?identity@0test@@YAXXZ) 中被引用
1 identity.obj : 错误 LNK2019: 未解析的外部符号 void __cdecl test::identity::make(void) (?make@identity@test@@YAXXZ),该符号在函数 void __cdecl test::identity::identity(void) (?identity@0test@@YAXXZ) 中被引用
1 range.obj : 错误 LNK2019: 未解析的外部符号 void __cdecl test::range::is(void) (?is@range@test@@YAXXZ),该符号在函数 void __cdecl test::range::range(void) (?range@0test@@YAXXZ) 中被引用

链接错误总是很难调试...但有未解析引用,所以我进行了检查...但源代码形式良好...最后我想到了:

我的文件夹层次结构看起来像这样:

src/
  identity/
    is.cpp
    make.cpp
    view.cpp
  range/
    is.cpp
    make.cpp
    view.cpp

解决方案中的层次结构也是如此(我总是将其设置为模仿“真实”文件夹结构)。

诊断输出信息如下:

Debug\is.obj
Debug\make.obj
Debug\view.obj

除了警告提示外,还会提示链接器已经两次传递了.obj文件,并且其中一个将被忽略。

不用再搜索了:Visual已经整齐地扁平化了我的文件夹层次结构,因此无法整洁地编译源代码。

目前,我只考虑将文件重命名,应该可以解决这个问题...

...但是有没有办法让Visual Studio不扁平化文件层次结构呢?


3
刚刚遇到了同样的问题,很烦人,因为我们需要手动“修复”它。很高兴你比我先问了。 :) - GManNickG
5
很久以前我放弃了在SO上搜索。 :) 我用谷歌代替。 - GManNickG
42
我刚刚在 VS 2013 中解决了一个类似的问题。对我来说,问题是一个头文件被编译成了独立的 C++ 文件。所以我最终得到了两个同名的目标文件:一个是 foo.cpp 的,一个是 foo.h 的。解决方法是转到 foo.h 的适当页面,并将“配置属性”->“常规”->“项类型”更改为“C/C++ 头文件”,然后进行清理重新构建。 - Adrian McCarthy
2
@AdrianMcCarthy 我遇到了同样的问题,你的建议解决了它。 - trenki
3
@AdrianMcCarthy的评论就是解决方案。这可能是由于添加->“新项目”向导自动设置文件的项目类型引起的。 - Dustin Biser
显示剩余3条评论
11个回答

159
我遇到了一个类似的链接器警告问题,警告信息为“LNK4042:重复指定对象;将忽略额外项”。在我的情况下,Visual Studio试图编译同名的头文件和源文件 - MyClass.h和MyClass.cpp。这是因为我将.cpp文件重命名为.h文件,导致Visual Studio混淆了。通过查看Debug目录中的编译器日志,我发现了问题。要解决问题,只需从项目中删除.h文件,然后再次添加即可。

39
您可以在解决方案资源管理器中右键单击“foo.h”文件,然后将“项类型”设置为“C/C++头文件”,而不是“C/C++编译器”。 - Thomas Eding
作为另一种选择,您也可以从vcxproj文件中删除<ClCompile Include="foo.h" />行。 - Yaur
4
@Yaur - 或者更好的方法是将其更改为“CLInclude”条目。 - T.E.D.

102

我想转贴我认为的答案,如果你打开整个项目的属性,并在 C/C++ -> 输出文件 ->“对象文件名称” 下更改值为以下内容:

$(IntDir)/%(RelativeDir)/

在 VS 2010 中,我相信这将消除所有对象文件的歧义(因为我相信在任何疯狂的情况下,Windows 都不会让你在同一个目录中拥有两个同名文件)。请在此处查看详细信息。


7
补充一下:似乎%(RelativeDir)在路径中不会去掉任何../..(虽然它不应该去掉,但也没有其他选择),所以你可能要添加"假"目录才能让文件在“正确”的目录中构建。例如,我有$(IntDir)/a/a/%(RelativeDir)/,只是为了让它可以在$(IntDir)中构建,因为我的.cpp文件的路径中有两个../(路径相对于$(ProjectDir),我想)。还要注意,%(RelativeDir)前面有一个%而$(IntDir)前面有一个$(答案是正确的,只是读得快可能会错过这一点)。 - n1ckp
2
嗯...我想知道为什么这不是默认设置。好吧,我猜我只需要把这个添加到每个项目中(几乎没有什么可以编译而不需要这个修复)。 - Navin
这在Visual Studio 2012更新1中有效,但自从我升级到更新4后,VS似乎不再想为目标文件创建中间目录了。 :((请参见http://stackoverflow.com/questions/30212698。) - Thomas Young
当我在CLI中使用cl.exe时该怎么做? - Learning
在VS2013中没有效果。 - Sandburg
显示剩余2条评论

9
在解决方案资源管理器窗口中右键单击.cpp文件,选择属性,C/C++,输出文件,对象文件名称设置。默认值为$(IntDir)\,这就是所谓的压平操作。所有.obj文件都将进入$(IntDir),即调试配置下的“Debug”目录。
您可以更改设置,例如$(IntDir)\is2.obj。或者选择一个组的所有文件(使用Shift + 单击)并将设置更改为$(IntDir)\identity\ 或者您可以更改.cpp文件名,以便.obj文件不互相覆盖。在两个目录中具有完全相同名称的文件有点奇怪。
或者您可以创建多个项目,例如为identity和range中的文件创建.lib项目。例如,在makefile项目中通常会这样做。但是,这使得管理编译和链接设置变得更加麻烦,除非您使用项目属性表。

谢谢,我已经重命名文件了,因为这是最简单的方法。没有办法让 Visual 保留我在项目中精心构建的层次结构吗?我知道给几个文件使用相同的文件名很奇怪,但我更喜欢按子目录聚类,而不是给文件加前缀...而且,在子目录中放置它们并添加子目录名称作为前缀是多余的! - Matthieu M.
您可以同时更改多个文件的设置。按住CTRL键并单击以选择它们。上次我尝试使用$(IntDir)\$(ParentName)\时遇到了麻烦。 - Hans Passant
@Hans:那我猜我会继续使用不同的名称,虽然我对这个解决方案并不是很满意,但既然它只是用于单元测试部分,我想我还是能接受的。 - Matthieu M.
能否在属性表中针对每个文件设置$(IntDir)?我知道可以在属性表中为整个项目设置它,但我不知道是否可以根据正在编译的文件路径进行设置。(我的猜测是不行,但我是一个完全的MSBuild新手) - James McNellis
@James,项目属性表具有项目范围,它们会影响所有文件。 - Hans Passant
@Hans:我也是这么想的。有点遗憾,如果能够有条件地为某些文件设置属性就太好了。谢谢。 - James McNellis

8

右击头文件 -> 属性 -> ItemType(选择 C/C++ Header)。对于Cpp文件也做同样操作,但选择C/C++编译器(这对我很有效)。


这是我最后想到要寻找的东西。非常感谢。 - McLeary

4

除了删除并创建新文件之外,您还可以更改编译/包含设置。

进入您的project.vcxproj文件,用编辑器打开它,找到类似html的行<ItemGroup>

它应该看起来像:

<ItemGroup>
<ClCompile Include="implementation.cpp" />
</ItemGroup>

并且

<ItemGroup>
<ClInclude Include="declaration.hpp" />
</ItemGroup>`

假设您的实现文件是 .cpp,声明文件是 .hpp。确保所有的实现文件都在第一个 <ItemGroup> 部分中列出,如果有多个,则同样适用于第二个 <ItemGroup> 部分中的多个声明文件。

3

我遇到了stdafx.cpp的问题。一些奇怪的情况下,stdafx.cpp被复制了一份,导致出现了第二个StdAfx.cpp文件(注意大小写不同)。

在删除了StdAfx.cpp之后,一切工作正常!

使用VS 2010。


遇到一个类似的问题,但是不是文件重复了,而是同一个文件在 ClCompile ItemGroup 中列出了两次。 - Nicholas Betsworth

3
我在C/C++ -> 输出文件 -> “对象文件名”下使用了$(IntDir)\%(Directory)\。

虽然本质上与被接受的答案相同,但使用%(Directory)而不是%(RelativeDir)会更安全一些。正如被接受的答案评论中n1ckp所指出的那样,取决于项目在磁盘上的结构,相对目录可能最终会将.obj文件放在意想不到的位置。 - user1593842

2
我曾在同一个项目中使用了带有相同文件名的.c.cpp文件。这些文件散布在各个文件夹中,其他人提供的解决方案会导致混乱和文件夹噩梦(在我的情况下)。即使是Release版本也会覆盖Debug版本!
一个好的(不完美的)解决方案是使用$(ParentName),但出于某种原因,它已经被从Visual Studio的后续版本(2015+)中删除了。
现在我成功使用的是: $(IntDir)%(Filename)%(Extension).obj
这至少将编译后的.c对象文件与.cpp分开。

0

我解决了这个问题,通过修改我的项目中的文件名。有两个文件都叫main.c和main.cpp。我改变其中一个文件名后,问题得到了解决。


0

我刚刚克服了一个类似的错误信息,以及通过以下步骤解决了更多问题。症状:每个特定头文件中定义的每个函数调用都会产生一个链接器错误,以及在输出末尾为头文件中定义的每个函数产生一个错误。

然后我想起来,当我最初创建这个头文件时,我不小心选择了“添加->新项目->C++文件”,虽然我将其命名为'whatever.h',但由于我使用的不正确的操作,Visual Studio认为它们是相同类型的文件。检查构建输出日志使这一点显而易见。

解决方案(使用VS Community 2019)

  1. 先备份项目(以防万一)。
  2. 右键单击有问题的头文件,选择“从项目中排除”(这不会删除它们;VS 项目只是忽略它们)。
  3. 对于相应的 .c 或 .cpp 文件也做同样的操作。
  4. 在项目上执行“生成”->“清理”
  5. 在项目上执行“生成”->“重新生成” -- 当然会出现错误---
  6. 右键单击“头文件”->“添加”->“现有项”,然后选择 .h 文件
  7. 右键单击“源文件”->“添加”->“现有项”,然后选择 .c 或 .cpp 文件
  8. 在项目上执行“生成”->“重新生成”。

这对我完全解决了问题,消除了许多令人烦恼的链接器错误,包括本问题的标题 LNK4042。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接