带有 Clang 10 显式模板实例化的 ~queue 的未定义引用
Posted
技术标签:
【中文标题】带有 Clang 10 显式模板实例化的 ~queue 的未定义引用【英文标题】:Undefined reference for ~queue with explicit template instantiation with Clang 10 【发布时间】:2020-12-14 08:07:51 【问题描述】:以下代码未链接 Clang 10,但成功链接 GCC 和 Clang 9:
#include <queue>
template <typename T>
class A
public:
void f();
private:
std::queue<int> q;
;
template <typename T>
void A<T>::f()
q = ;
template class A<int>;
int main()
return 0;
我从编译器得到的是:
Online example
/opt/compiler-explorer/gcc-9.3.0/lib/gcc/x86_64-linux-gnu/9.3.0/../../../../x86_64-linux-gnu/bin/ld: /tmp/example-f70f65.o: in function `A<int>::f()':
/home/ce/<source>:16: undefined reference to `std::queue<int, std::deque<int, std::allocator<int> > >::~queue()'
clang-10: error: linker command failed with exit code 1 (use -v to see invocation)
Compiler returned: 1
如果我将std::queue
替换为std::vector
、std::deque
或std::set
,它会起作用;或者如果我删除显式模板实例化。
如果我将 q =
替换为完整的构造函数调用 q = std::queue<int>
,它也可以工作。
这段代码不标准还是编译器/libc++ 错误?
【问题讨论】:
【参考方案1】:我不确定您为什么会收到这样的链接器错误,也许它是 Godbolt 的一些独特问题。如果您尝试使用 coliru 编译代码:https://coliru.stacked-crooked.com/a/ac9c188334f858d8,您将收到一个编译时错误,表明您尝试使用队列的列表初始化:
main.cpp:16:7: error: no viable overloaded '='
q = ;
~ ^ ~~
main.cpp:19:16: note: in instantiation of member function 'A<int>::f' requested here
template class A<int>;
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.5.0/../../../../include/c++/5.5.0/bits/stl_queue.h:96:11: note: candidate function (the implicit move assignment operator) not viable: cannot convert initializer list argument to 'std::queue<int, std::deque<int, std::allocator<int> > >'
class queue
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.5.0/../../../../include/c++/5.5.0/bits/stl_queue.h:96:11: note: candidate function (the implicit copy assignment operator) not viable: cannot convert initializer list argument to 'const std::queue<int, std::deque<int, std::allocator<int> > >'
1 error generated.
队列不允许使用 initializer_list 进行列表初始化,这里有 SO:Why can't I construct a queue/stack with brace-enclosed initializer lists? (C++11)
但是,如果您使用 libc++ (-stdlib=libc++),您似乎可以编译您的代码(至少在 coliru 上,我在 Godbolt 上尝试过但没有成功):https://coliru.stacked-crooked.com/a/df9d859a239843cf
它可能无法准确回答您的问题,但我的评论太长了。你也可以在这里找到类似的线程:https://github.com/envoyproxy/envoy/issues/9106
[编辑] 有趣的是,在重置 Godbolt UI 并使用相同的配置(https://godbolt.org/z/TzE9h9)再次输入代码后一切正常。
【讨论】:
我不认为队列的列表初始化是这里的问题,因为我的大括号是空的。他们应该将这些隐式转换为std::queue
,根据cppreference,自C++11 起允许这样做。但我认为 Coliru Clang 编译器已经过时了,因为 std::queue
默认构造函数被标记为显式 here,这就是我原来的示例无法编译的原因。
@PJK136 是的,可能就是这样。 Godbolg 显示 operator=(std::queue<int, std::deque<int, std::allocator<int> > >&&)
正在行中调用 q = ;
。你看到为什么godbolt.org/z/TzE9h9 编译而你的链接的godbold 链接没有编译的原因吗?
是的,这是因为在“输出”菜单中,我选中了“编译为二进制”,而在您的示例中,您没有(这是默认行为)。当您不检查时,我认为编译器不会链接任何东西(就像“-c”选项),所以没有链接错误。你可以试试这个简单的例子:godbolt.org/z/v7xaEq
我认为这是编译器的问题,它不会为队列生成析构函数,但后来它在链接时使用它。如果您在文件顶部的某个位置实例化std::queue<int>
somwhere,它将链接,因为将生成析构函数。这可能与 c++17 中添加的强制复制省略有关,但这只是我的猜测。
我看到这实际上是 10 版本中的更改:“尊重 C++17 复制省略;以前它会为省略的临时对象生成析构函数调用,包括在初始化和返回语句中。”和“不要为语句表达式生成重复的析构函数调用。”以上是关于带有 Clang 10 显式模板实例化的 ~queue 的未定义引用的主要内容,如果未能解决你的问题,请参考以下文章