[英]C++03 linker “already defined symbol” doesn't appear on intermediate file
我在visual studio 2005上的一個大型項目遇到了問題,我已經用完了很多想法。
我甚至無法提供一個有效的代碼片段,因為我不知道有什么相關,但我會嘗試:
我需要讓我的項目中的每個.cpp文件都有自己的ID號,並創建一個知道該ID的對象(可全局訪問)的實例。 我在此主題上接受了關於已接受答案的幫助如何在c ++中管理文件唯一ID並使其在沙箱環境中工作。
添加文件,為它們提供唯一的#define FILEID (FileId::ID_FileName)
然后訪問它們的實例在沙盒上工作正常。
現在出現了麻煩 - 我粘貼了使文件知道他們的IDS的代碼到主項目,並編譯。
到現在為止還挺好。
現在,我添加到項目中現有的.cpp文件之一:
#include "ids.h"
#define FILEID File1 // The FileId corresponding to this file
#include "global.h"
仍然編譯,鏈接,一切都很好。
將這些行添加到項目中的(任意)第二個.cpp文件現在會出現鏈接錯誤:
其中:
in name2.obj : error LNK2005: "public static class Instance & __cdecl Manager<3>::getInstance(void)" (?getInstance@$Manager@$02@@SAAAVInstance@@XZ) already defined in name1.obj
的錯誤 in name2.obj : error LNK2005: "public static class Instance & __cdecl Manager<3>::getInstance(void)" (?getInstance@$Manager@$02@@SAAAVInstance@@XZ) already defined in name1.obj
有時錯誤僅在第二個文件中,有時(在沒有更改的連續構建之間)錯誤出現在文件夾中的每個.cpp文件中。
查看我添加行的文件的中間文件(預處理器輸出)顯示了該文件的一個外觀
template <>
Instance &Manager<FILEID>::getInstance()
{
static Instance theInstance = getTheFactory().getInstance(FILEID);
return theInstance;
};
使用正確的FileId :: ID_FileName,這是與其他文件不同的名稱。 仍然,鏈接器認為在多個文件中使用相同的FileId。
在不相關的文件(也給出完全相同的錯誤)上,根本沒有getInstance()
出現。 顯然,聯系人不應該在那里大喊大叫。
我查了一下,沒有.cpp文件在項目的某個地方互相包含。
我完全沒有想到可能導致這種情況的想法,並希望得到任何幫助。
編輯1
ids.h
enum FileId{
ID_file1ID=3,//just to see a non zero number in the debugger, which I do
ID_file2ID,
//and so on
FileIdSize
}
編輯2
當這些錯誤開始時,編譯器開始表現得非常意外。
將行sdfsdfgasaedfahjk
添加到任何文件仍然是編譯和通過。
它清楚地說明了已添加到該行的文件名以進行編譯。 它明確指出它與它的聯系。 它過去了。
我現在不能相信編譯器。
不知道發生了什么。
您有2個cpp文件將FILEID
定義為相同的值3
。
至於MCVE:
ids.h:
#pragma once
#define File1 3
#define File2 3 //<--same value on purpose
global.h
struct Instance
{
};
struct Factory
{
Instance getInstance(int FileID) { return Instance(); }
};
template <int ID>
struct Manager
{
Factory factory;
Instance& getInstance();
Factory& getTheFactory() { return factory; }
};
template <>
Instance& Manager<FILEID>::getInstance()
{
static Instance theInstance = getTheFactory().getInstance(FILEID);
return theInstance;
};
name1.cpp
#include "ids.h"
#define FILEID File1 // The FileId corresponding to this file
#include "global.h"
name2.cpp
#include "ids.h"
#define FILEID File2 // The FileId corresponding to this file
#include "global.h"
在編譯時,為name1.cpp
和name2.cpp
創建了一個特殊的Manager<3>::getInstance(void)
name2.cpp
。
您不能在2個不同的編譯單元中為FILEID
使用相同的值。
編輯:編譯時檢查值
需要預處理器定義__BASE_FILE__="%(Filename)%(Extension)"
(配置屬性 - > C / C ++ - >預處理器 - >預處理器定義)
template <>
Instance& Manager<FILEID>::getInstance()
{
#define _STR(x) #x
#define STR(x) _STR(x)
#define CHECK_ID() __pragma(message("Initializing \"Instance& Manager<FILEID>::getInstance()\" with FILEID="STR(FILEID)" in "STR(__BASE_FILE__)))
CHECK_ID()
static Instance theInstance = getTheFactory().getInstance(FILEID);
return theInstance;
};
示例 - 輸出:
1>------Build started : Project : Test_Call, Configuration : Debug Win32------
1> name1.cpp
1> Initializing "Instance& Manager<FILEID>::getInstance()" with FILEID = FileId::ID_file1ID in "name1.cpp"
1> name2.cpp
1> Initializing "Instance& Manager<FILEID>::getInstance()" with FILEID = FileId::ID_file2ID in "name2.cpp"
1> Test_Call.vcxproj-><Project>\Debug\Test_Call.exe
== == == == == Build: 1 succeeded, 0 failed, 0 up - to - date, 0 skipped == == == == ==
編輯:使用FileId值作為模板參數(MSVE)
id.h
#pragma once
enum FileId {
ID_file1ID = 3,//just to see a non zero number in the debugger, which I do
ID_file2ID,
//and so on
FileIdSize
};
global.h
#pragma once
#include "ids.h"
struct Instance
{
};
struct Factory
{
Instance getInstance(int FileID) { return Instance(); }
};
template <FileId ID>
struct Manager
{
static const FileId manager_id = ID;
static Factory& getTheFactory() { return m_factory; }
static Instance& getInstance()
{
static Instance theInstance = getTheFactory().getInstance(manager_id);
return theInstance;
}
private:
static Factory m_factory;
};
global.cpp
#include "global.h"
Factory Manager<FileId::ID_file1ID>::m_factory;
Factory Manager<FileId::ID_file2ID>::m_factory;
name1.cpp
#include "global.h"
void test1()
{
Instance& a = Manager<FileId::ID_file1ID>::getInstance();
}
name2.cpp
#include "global.h"
void test2()
{
Instance& a = Manager<FileId::ID_file2ID>::getInstance();
}
TEST.CPP
#include <iostream>
#include "global.h"
using namespace std;
int main(int argc, char** argv)
{
Instance& a = Manager<FileId::ID_file1ID>::getInstance();
Instance& b = Manager<FileId::ID_file2ID>::getInstance();
Instance& c = Manager<FileId::ID_file1ID>::getInstance();
Instance* aptr = &a;
Instance* bptr = &b;
Instance* cptr = &c;
printf("aptr==bptr -> %s\n", (aptr == bptr) ? "true" : "false"); //->false
printf("aptr==cptr -> %s\n", (aptr == cptr) ? "true" : "false"); //->true (both use the instance from ID_file1ID
printf("bptr==cptr -> %s\n", (bptr == cptr) ? "true" : "false"); //->false
}
這不是一個答案,但可能有助於找出問題所在。
以下代碼與原始答案基本相同,但由於在各個地方需要樣板代碼的昂貴,所有復雜性都被剝奪了。
idmanager.h
struct Instance {/*...*/}; Instance &getFile1Instance(); Instance &getFile2Instance(); // etc...
idmanager.cpp
Instance &getFile1Instance() { static Instance file1instance; return file1instance; } Instance &getFile2Instance() { static Instance file2instance; return file2instance; } // etc...
在每個文件中,放在開頭
#include "idmanager.h"
並且您可以以顯而易見的方式獲取任何文件的靜態Instance
。
這很簡單,因此將其復制到項目中不會導致問題。
如果上面的示例有效,那么嘗試使其稍微接近原始答案:將getFileXInstance
函數的定義移動到文件本身,並刪除idmanager.cpp
。
idmanager.h
struct Instance {/*...*/}; Instance &getFile1Instance(); Instance &getFile2Instance(); // etc...
file1.cpp
#include "idmanager.h" Instance &getFile1Instance() { static Instance file1instance; return file1instance; }
file2.cpp
// etc...
顯然,這只是在不同的.obj文件之間移動代碼,所以應該仍然有效。
現在用帶有單個靜態成員函數getInstance
的struct
替換每個getFileXInstance
函數,如下所示:
idmanager.h
struct Instance {/*...*/}; struct Manager1 { static Instance &getInstance(); // defined in file1.cpp }; struct Manager2 { static Instance &getInstance(); // defined in file2.cpp }; // etc...
file1.cpp
#include "idmanager.h" Instance &Manager1::getInstance() { static Instance file1instance; return file1instance; }
file2.cpp
// etc...
上一步允許我們使用模板減少樣板代碼的數量:
idmanager.h
struct Instance {/*...*/}; template <int id> struct Manager { static Instance &getInstance(); // each instantiation has its definition in a different cpp file };
file1.cpp
#include "idmanager.h" template <> Instance &Manager<1>::getInstance() { static Instance file1instance; return file1instance; }
這是鏈接器錯誤最有可能再次出現的地方,如果它們完全出現的話。
通過將公共代碼放在共享頭globals.h
,並將預處理器常量FILEID
給它,也可以刪除更多重復。
idmanager.h
struct Instance {/*...*/}; template <int id> struct Manager { static Instance &getInstance(); // each instantiation has its definition in a different cpp file };
file1.cpp
#include "idmanager.h" #define FILEID 1 #include "globals.h"
globals.h
template <> Instance &Manager<FILEID>::getInstance() { static Instance theInstance; return theInstance; }
最后一個示例現在與原始答案相同,但有一些差異(沒有工廠,沒有枚舉,沒有getThisFileInstance()
)與鏈接器錯誤無關。 因此(假設第一個示例有效)您可以確定哪個更改破壞了程序,這應該有助於診斷真正的問題。
(注意:雖然你的錯誤正好是多個文件共享同一個id所出現的錯誤,但我認為這不是這種情況。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.