如何解决使用Clang
// foo.h
#pragma once
template <typename T> const T foo;
template <>
const int foo<int> = 1;
// a.cpp
#include "foo.h"
int main() {}
// b.cpp
#include "foo.h"
如果我在Linux上使用GCC 7.5.0构建这些文件,则可以正常工作:
$ g++ -std=c++17 a.cpp b.cpp -o out
$
但是在Mac上使用Apple Clang 12.0.0时,会出现此错误:
$ clang++ -std=c++17 a.cpp b.cpp -o out
duplicate symbol 'foo<int>' in:
/var/folders/g5/8twmk1xj481_6btvppyw5j4h0000gp/T/a-62bdde.o
/var/folders/g5/8twmk1xj481_6btvppyw5j4h0000gp/T/b-ea4997.o
ld: 1 duplicate symbol for architecture x86_64
解决方法
它违反了ODR,应为inline
(自C ++ 17起)或位于未命名的namespace
中(自C ++ 11起)。
为什么违反ODR:
在整个程序(包括任何标准库和用户定义库)中,都需要出现每个非内联函数或变量的唯一且仅一个定义(请参见下文)。不需要编译器来诊断这种违反情况,但是未定义违反该行为的程序的行为。
由于foo.h
和a.cpp
都包含b.cpp
,因此在每个翻译单元foo<int>
和a.cpp
中都定义了变量b.cpp
。因此该程序中有2个定义,这违反了以前的规则,除非它是未使用的(这意味着不是 ODR使用的)或它是一个inline
变量。
您有2种解决方案:
- 将
template<> int const foo<int> = 1;
放入另一个foo.cpp
。然后它只有一个定义。 - 为
inline
添加foo
(自C ++ 17起)。inline
变量被允许在翻译单元中具有多个相同定义。如果不是在C ++ 17中,则可以将其放入未命名的namespace
中,这意味着将其中的所有内容都设为inline
。
RedFrog的答案是正确的-这违反了“单一定义规则”(ODR),但并没有真正解释为什么违反了ODR。在C ++中,全局const
变量具有内部(static
)链接,这将导致每个编译单元具有单独的变量实例,因此不会违反ODR。但是it turns out认为const
不会导致 template 变量具有内部链接:
未声明为extern的非局部非易失性非模板(自C ++ 14起)非内联(自C ++ 17起)声明时使用的const限定符为其提供内部链接。 / p>
有几种方法可以解决此问题。
使用static
的内部链接
您可以在模板变量上使用static
到gives them internal linkage:
在命名空间范围内声明的以下任何名称都有内部链接:
变量,变量模板(自C ++ 14起),函数或声明为静态的函数模板;
编辑:我不建议您使用此技术。如果将static
应用于声明和专业化,则它在Clang中可以正常工作,但GCC(至少到目前为止)抱怨
error: explicit template specialization cannot have a storage class
如果仅在声明中设置static
,则它会在GCC中编译,但是使用Clang会出现重复的符号错误。
使用namespace
的内部链接
将模板变量放在匿名名称空间中也会为其提供内部链接。
此外,在未命名的命名空间或未命名的命名空间中的命名空间中声明的所有名称,甚至是明确声明为extern的名称,都具有内部链接。
使用inline
禁用ODR
这有点不同。声明为inline
但未声明static
的模板变量仍然具有外部链接,但具有are allowed to have more than one definition。
具有外部链接(例如,未声明为静态)的内联函数或变量(自C ++ 17起)具有以下附加属性:
- 程序中的内联函数或变量(自C ++ 17起)可能有多个定义,只要每个定义出现在不同的翻译单元中(对于非静态内联函数和变量(自C起) ++ 17))所有定义都是相同的。例如,可以在包含在多个源文件中的头文件中定义内联函数或内联变量(自C ++ 17起)。
- 必须在每个翻译单元中内联声明。
- 每个翻译单位的地址都相同。
这意味着变量的inline
的行为类似于函数的inline
。
如有疑问,inline
可能是最佳选择。在非常简单的测试中,我完成了所有操作,即使在-O0时也都产生了相同的输出,但是理论上inline
保证程序中变量只有一个副本,因为它仍然具有外部链接,而其他方法没有。也许。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。