由于标头中的专业化初始化而避免重复的符号?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了由于标头中的专业化初始化而避免重复的符号?相关的知识,希望对你有一定的参考价值。
由于我试图在标题中提供的定义,我正在捕获重复的符号错误。这是来自Minimal, Complete, and Verifiable example的错误。头文件和源文件如下所示。
$ 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。这个问题有专业化,但它适用于MSVC而不是Clang:How to initialize a static member of a parametrized-template class。而这个问题表明将它放在源(* .cpp)文件中,但我们的目标是头文件避免Clang 3.8和'Id<S>::id' required here, but no definition is available
警告:Where should the definition of an explicit specialization of a class template be placed in C++?
初始化时,GCC,ICC,MSVC,SunCC和XLC都可以。 Clang和LLVM给了我麻烦。 Clang和LLVM在专门化和extern
的显式实例化方面也遇到了麻烦,所以它拥有特殊的地狱。
我们支持C ++ 03但是C ++ 17,所以我们必须小心这个解决方案。天真地,我尝试将特化的初始化放在一个未命名的命名空间中,以防止符号转义转换单元,但这导致了编译错误。
在标头中初始化和专门化模板类时,我们如何避免重复的符号定义?
下面是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;
}
啊
#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"
您可以使用unnamed namespace,它可以使您的类具有内部链接,例如
#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>
与另一个翻译单元中的a.cpp
不同,因此不违反单定义规则。
Clang / LLVM不是问题。您只是在没有诊断要求的情况下遇到未定义的行为。修复很简单。您需要将您的专业化设置在一个翻译单元中。即,
#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
然后是命令行:
template<> const uint8_t Id<S>::id = 0x01;
template<> const uint8_t Id<T>::id = 0x02;
瞧。有用。
您违反了Id :: id和Id :: id的ODR(一个定义规则)。它们会被包含在每个翻译单元中,因此会在您链接时显示。
根据你对S级和T级id的意图,你必须给他们一个独特的家。一个可能是将它们停放在main.cpp中。 main.cpp添加
#include "s.h"
#include "a.h"
template<> const uint8_t Id<S>::id = 0x01;
或者,将s的id放在s.cpp中,并将t的id放在t.cpp中:
s.cpp
qazxswpoi
和t.cpp的等价物。
不要忘记在a.h中删除任何S和T的痕迹。
但是,如果它们是a.h接口的一部分,则创建一个a.cpp并在那里定义它们。
以上是关于由于标头中的专业化初始化而避免重复的符号?的主要内容,如果未能解决你的问题,请参考以下文章
需要一种有效的方法来避免使用 Laravel 5 重复代码片段
Android TV(leanback)中的持久标头片段(禁用动画)
Angular 5-由于请求的资源上不存在“没有'Access-Control-Allow-Origin'标头”而出现错误[重复]