Win32コンソールアプリケーションにA.cppとB.cppの2つのファイルがあります。
2つのファイルには、次の2行のコードのみが含まれています。
#include "stdafx.h"
int k;
コンパイルするとエラーが発生します
Error 1 error LNK2005: "int k" (?a@@3HA) already defined in A.obj
何が起こっているのかわかりません。
誰かが私にこれを説明してもらえますか?
Win32コンソールアプリケーションにA.cppとB.cppの2つのファイルがあります。
2つのファイルには、次の2行のコードのみが含まれています。
#include "stdafx.h"
int k;
コンパイルするとエラーが発生します
Error 1 error LNK2005: "int k" (?a@@3HA) already defined in A.obj
何が起こっているのかわかりません。
誰かが私にこれを説明してもらえますか?
なぜこのエラーですか?
1つの定義規則に違反したため、リンクエラーが発生しました 。
推奨される解決策:
2つのcppファイルで同じ名前の変数が必要な場合は、エラーを回避するために名前のない名前空間(匿名の名前空間)を使用する必要があります。
namespace
{
int k;
}
複数のファイル間で同じ変数を共有する必要がある場合は、を使用する必要がありますextern
。
ああ
extern int k;
A.cpp
#include "A.h"
int k = 0;
B.cpp
#include "A.h"
//Use `k` anywhere in the file
プロジェクトの設定で /FORCE:MULTIPLE
、リンカーのコマンドラインオプションに追加します。
MSDNから:「LINKがシンボルの複数の定義を検出するかどうかに関係なく、/ FORCE:MULTIPLEを使用して出力ファイルを作成します。」
両方が同じ変数を参照するようにする場合は、一方にが、もう一方にint k;
が必要です。extern int k;
この状況では、通常、定義(int k;
)を1つの.cpp
ファイルに入れ、宣言(extern int k;
)をヘッダーに入れて、その変数にアクセスする必要がある場所に含めます。
k
それぞれがたまたま同じ名前を持つ別々の変数にしたい場合は、次static
のようにマークを付けることができますstatic int k;
(すべてのファイル、または少なくとも1つのファイルを除くすべて)。または、匿名の名前空間を使用することもできます。
namespace {
int k;
};
繰り返しますが、多くても1つのファイルを除くすべてのファイルで。
Cでは、コンパイラーは一般的にこれについてそれほど気難しいものではありません。int k;
具体的には、Cには「仮定義」の概念があるため、 (同じまたは別々のソースファイルに)2回のようなものがある場合、それぞれが仮定義として扱われ、それらの間に競合は発生しません。ただし、両方に初期化子を含む2つの定義を設定することはできないため、これは少し混乱する可能性があります。初期化子を含む定義は、暫定的な定義ではなく、常に完全な定義です。言い換えれば、int k = 1;
2回出現することはエラーになりますがint k;
、ある場所とint k = 1;
別の場所ではエラーになりません。この場合、はint k;
暫定的な定義として扱われ、は定義として扱われint k = 1;
ます(両方とも同じ変数を参照します)。
異なる.cppファイルで「k」を異なる値にしたい場合(したがって、2回宣言する)、両方のファイルを次のように変更してみてください。
namespace {
int k;
}
これにより、名前「k」が翻訳単位全体で「k」を一意に識別することが保証されます。古いバージョンstatic int k;
は非推奨です。
同じ値を指すようにする場合は、1つをに変更しextern int k;
ます。
どちらのファイルも、変数k
を整数(int
)として定義しています。
その結果、リンカは同じ名前の2つの変数を認識し、を参照する場合にどちらを使用すべきかがわかりませんk
。
これを修正するには、宣言の1つを次のように変更します。
extern int k;
つまり、「kは整数であり、ここで宣言されていますが、外部で定義されています(つまり、他のファイル)。」
k
これで、2つの異なるファイルから適切に参照できる変数が1つだけになりました。
また、これらの変換ユニットでこの変数を共有する場合は、A.cppで定義int k;
し、B.cppに配置extern int k;
します。
int k;
ヘッダーファイルに存在すると、k
このヘッダーが含まれる各変換ユニット内でシンボルが定義されますが、リンカーはシンボルが1回だけ定義されることを想定しています(別名、単一定義規則違反)。
関与する提案extern
は間違いでextern
はありませんが、C-ismであり、使用しないでください。
ODR違反を引き起こすことなくヘッダーファイルの変数を複数の変換単位で定義できるようにするC++17以前のソリューションは、テンプレートへの変換です。
template<typename x_Dummy = void> class
t_HeaderVariableHolder
{
public: static int s_k;
};
template<typename x_Dummy> int t_HeaderVariableHolder<x_Dummy>::s_k{};
// Getter is necessary to decouple variable storage implementation details from access to it.
inline int & Get_K() noexcept
{
return t_HeaderVariableHolder<>::s_k;
}
inline
C ++ 17では、変数を使用できるため、作業がはるかに簡単になります。
inline int g_k{};
// Getter is necessary to decouple variable storage implementation details from access to it.
inline int & Get_K() noexcept
{
return g_k;
}
リンカは、変数がk
複数回定義されていることを通知します。実際、A.cppに定義があり、B.cppに別の定義があります。両方のコンパイルユニットは、リンカがプログラムの作成に使用する対応するオブジェクトファイルを生成します。問題は、あなたの場合、リンカーが使用する定義を知らないことk
です。C ++では、同じ構成(変数、型、関数)の定義を1つだけ持つことができます。
それを修正するには、あなたの目標が何であるかを決定する必要があります
k
、両方の.cppファイルで匿名の名前空間を使用して、k
現在行っているように参照できます。。
namespace {
int k;
}
k
して、定義の重複を避けることができます。k
両方の.cppファイルで一度だけ定義して使用したい場合は、一方をとして宣言し、もう一方extern int k;
はそのままにしておく必要があります。これにより、リンカはどちらの場合も1つの定義(変更されていないバージョン)を使用するようになります。これはextern
、変数が別のコンパイル単位で定義されていることを意味します。