由于 header 中的专业化初始化,避免了重复符号?
Avoiding duplicate symbol due to initialization of specialization in header?
由于我试图在 header 中提供的定义,我发现了重复的符号错误。这是来自 Minimal, Complete, and Verifiable example 的错误。 header 个文件和源文件如下所示。
$ clang++ main.cpp x.cpp y.cpp -o main.exe 2>&1 | c++filt
duplicate symbol Id<S>::id in:
/tmp/main-3f2415.o
/tmp/long long-d62c28.o
duplicate symbol Id<T>::id in:
/tmp/main-3f2415.o
/tmp/long long-d62c28.o
duplicate symbol Id<S>::id in:
/tmp/main-3f2415.o
/tmp/unsigned long long-bfa6de.o
duplicate symbol Id<T>::id in:
/tmp/main-3f2415.o
/tmp/unsigned long long-bfa6de.o
ld: 4 duplicate symbols for architecture x86_64
这里有一个类似的问题,但不涉及专业化:Static member initialization in a class template. This question has the specialization but it is for MSVC and not Clang: How to initialize a static member of a parametrized-template class. And this question states to put it in the source (*.cpp) file but we are aiming for the header file to avoid Clang 3.8 and 'Id<S>::id' required here, but no definition is available
warnings:
GCC、ICC、MSVC、SunCC 和 XLC 都可以初始化。 Clang 和 LLVM 给我带来了麻烦。 Clang 和 LLVM 在特化和 extern
的显式实例化方面也有问题,所以它有自己的特殊地狱。
我们支持C++03到C++17,所以我们必须小心解决方案。我天真地尝试将专业化的初始化放在一个未命名的命名空间中,以防止符号转义翻译单元,但它导致了编译错误。
在 header 中初始化和特化模板 class 时,我们如何避免重复的符号定义?
下面是MCVE,这是一个cat main.cpp a.h s.h s.cpp t.h t.cpp x.cpp y.cpp
。问题似乎出在 a.h
,它提供了专门化和初始化;以及源文件 x.cpp
和 y.cpp
,其中包括 a.h
.
main.cpp
#include "a.h"
#include "s.h"
#include "t.h"
int main(int argc, char* argv[])
{
uint8_t s = Id<S>::id;
uint8_t t = Id<T>::id;
return 0;
}
a.h
#ifndef A_INCLUDED
#define A_INCLUDED
#include <stdint.h>
template <class T> class Id
{
public:
static const uint8_t id;
};
class S;
class T;
template<> const uint8_t Id<S>::id = 0x01;
template<> const uint8_t Id<T>::id = 0x02;
#endif
s.h
#ifndef S_INCLUDED
#define S_INCLUDED
class S {
public:
S();
};
#endif
s.cpp
#include "s.h"
S::S() {}
t.h
#ifndef T_INCLUDED
#define T_INCLUDED
class T {
public:
T();
};
#endif
t.cpp
#include "t.h"
T::T() {}
x.cpp
#include "a.h"
y.cpp
#include "a.h"
Clang/LLVM 不是问题。您只是 运行 进入未定义的行为,无需诊断。修复很简单。您需要将您的专业放在一个 翻译单元中。即,
a.cpp
:
#include "a.h"
template<> const uint8_t Id<S>::id = 0x01;
template<> const uint8_t Id<T>::id = 0x02;
然后命令行:
clang++ main.cpp a.cpp x.cpp y.cpp -o main.exe 2>&1 | c++filt
瞧。有效。
您违反了 Id::id 和 Id::id 的 ODR(一个定义规则)。它们为它们包含的每个翻译单元定义,因此在您 link.
时显示
根据您对 class S 和 T 的 id 的意图,您必须给它们一个独特的家。一种可能是将它们停在 main.cpp。
main.cpp 添加
template<> const uint8_t Id<S>::id = 0x01;
template<> const uint8_t Id<T>::id = 0x02;
或,把S的id放在s.cpp,T的id放在t.cpp:
s.cpp
#include "s.h"
#include "a.h"
template<> const uint8_t Id<S>::id = 0x01;
和 t.cpp.
的等价物
不要忘记删除 a.h 中任何 S 和 T 的痕迹。
但是,如果它们是a.h接口的一部分,创建一个a.cpp并在那里定义它们。
您可以使用 unnamed namespace 使您的 class 具有内部链接,例如
#ifndef A_INCLUDED
#define A_INCLUDED
#include <stdint.h>
namespace {
template <class T> class Id
{
public:
static const uint8_t id;
};
}
class S;
class T;
template<> const uint8_t Id<S>::id = 0x01;
template<> const uint8_t Id<T>::id = 0x02;
#endif
将此代码替换为您示例中 a.h
中的内容,然后它将起作用,因为由于内部链接,一个翻译单元中的 Id<T>
与另一个翻译单元中的不同,因此一个-未违反定义规则。
由于我试图在 header 中提供的定义,我发现了重复的符号错误。这是来自 Minimal, Complete, and Verifiable example 的错误。 header 个文件和源文件如下所示。
$ clang++ main.cpp x.cpp y.cpp -o main.exe 2>&1 | c++filt
duplicate symbol Id<S>::id in:
/tmp/main-3f2415.o
/tmp/long long-d62c28.o
duplicate symbol Id<T>::id in:
/tmp/main-3f2415.o
/tmp/long long-d62c28.o
duplicate symbol Id<S>::id in:
/tmp/main-3f2415.o
/tmp/unsigned long long-bfa6de.o
duplicate symbol Id<T>::id in:
/tmp/main-3f2415.o
/tmp/unsigned long long-bfa6de.o
ld: 4 duplicate symbols for architecture x86_64
这里有一个类似的问题,但不涉及专业化:Static member initialization in a class template. This question has the specialization but it is for MSVC and not Clang: How to initialize a static member of a parametrized-template class. And this question states to put it in the source (*.cpp) file but we are aiming for the header file to avoid Clang 3.8 and 'Id<S>::id' required here, but no definition is available
warnings:
GCC、ICC、MSVC、SunCC 和 XLC 都可以初始化。 Clang 和 LLVM 给我带来了麻烦。 Clang 和 LLVM 在特化和 extern
的显式实例化方面也有问题,所以它有自己的特殊地狱。
我们支持C++03到C++17,所以我们必须小心解决方案。我天真地尝试将专业化的初始化放在一个未命名的命名空间中,以防止符号转义翻译单元,但它导致了编译错误。
在 header 中初始化和特化模板 class 时,我们如何避免重复的符号定义?
下面是MCVE,这是一个cat main.cpp a.h s.h s.cpp t.h t.cpp x.cpp y.cpp
。问题似乎出在 a.h
,它提供了专门化和初始化;以及源文件 x.cpp
和 y.cpp
,其中包括 a.h
.
main.cpp
#include "a.h"
#include "s.h"
#include "t.h"
int main(int argc, char* argv[])
{
uint8_t s = Id<S>::id;
uint8_t t = Id<T>::id;
return 0;
}
a.h
#ifndef A_INCLUDED
#define A_INCLUDED
#include <stdint.h>
template <class T> class Id
{
public:
static const uint8_t id;
};
class S;
class T;
template<> const uint8_t Id<S>::id = 0x01;
template<> const uint8_t Id<T>::id = 0x02;
#endif
s.h
#ifndef S_INCLUDED
#define S_INCLUDED
class S {
public:
S();
};
#endif
s.cpp
#include "s.h"
S::S() {}
t.h
#ifndef T_INCLUDED
#define T_INCLUDED
class T {
public:
T();
};
#endif
t.cpp
#include "t.h"
T::T() {}
x.cpp
#include "a.h"
y.cpp
#include "a.h"
Clang/LLVM 不是问题。您只是 运行 进入未定义的行为,无需诊断。修复很简单。您需要将您的专业放在一个 翻译单元中。即,
a.cpp
:
#include "a.h"
template<> const uint8_t Id<S>::id = 0x01;
template<> const uint8_t Id<T>::id = 0x02;
然后命令行:
clang++ main.cpp a.cpp x.cpp y.cpp -o main.exe 2>&1 | c++filt
瞧。有效。
您违反了 Id::id 和 Id::id 的 ODR(一个定义规则)。它们为它们包含的每个翻译单元定义,因此在您 link.
时显示根据您对 class S 和 T 的 id 的意图,您必须给它们一个独特的家。一种可能是将它们停在 main.cpp。 main.cpp 添加
template<> const uint8_t Id<S>::id = 0x01;
template<> const uint8_t Id<T>::id = 0x02;
或,把S的id放在s.cpp,T的id放在t.cpp:
s.cpp
#include "s.h"
#include "a.h"
template<> const uint8_t Id<S>::id = 0x01;
和 t.cpp.
的等价物不要忘记删除 a.h 中任何 S 和 T 的痕迹。
但是,如果它们是a.h接口的一部分,创建一个a.cpp并在那里定义它们。
您可以使用 unnamed namespace 使您的 class 具有内部链接,例如
#ifndef A_INCLUDED
#define A_INCLUDED
#include <stdint.h>
namespace {
template <class T> class Id
{
public:
static const uint8_t id;
};
}
class S;
class T;
template<> const uint8_t Id<S>::id = 0x01;
template<> const uint8_t Id<T>::id = 0x02;
#endif
将此代码替换为您示例中 a.h
中的内容,然后它将起作用,因为由于内部链接,一个翻译单元中的 Id<T>
与另一个翻译单元中的不同,因此一个-未违反定义规则。