带有 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::vectorstd::dequestd::set,它会起作用;或者如果我删除显式模板实例化。

如果我将 q = 替换为完整的构造函数调用 q = std::queue&lt;int&gt;,它也可以工作。

这段代码不标准还是编译器/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&lt;int, std::deque&lt;int, std::allocator&lt;int&gt; &gt; &gt;&amp;&amp;) 正在行中调用 q = ;。你看到为什么godbolt.org/z/TzE9h9 编译而你的链接的godbold 链接没有编译的原因吗? 是的,这是因为在“输出”菜单中,我选中了“编译为二进制”,而在您的示例中,您没有(这是默认行为)。当您不检查时,我认为编译器不会链接任何东西(就像“-c”选项),所以没有链接错误。你可以试试这个简单的例子:godbolt.org/z/v7xaEq 我认为这是编译器的问题,它不会为队列生成析构函数,但后来它在链接时使用它。如果您在文件顶部的某个位置实例化std::queue&lt;int&gt; somwhere,它将链接,因为将生成析构函数。这可能与 c++17 中添加的强制复制省略有关,但这只是我的猜测。 我看到这实际上是 10 版本中的更改:“尊重 C++17 复制省略;以前它会为省略的临时对象生成析构函数调用,包括在初始化和返回语句中。”和“不要为语句表达式生成重复的析构函数调用。”

以上是关于带有 Clang 10 显式模板实例化的 ~queue 的未定义引用的主要内容,如果未能解决你的问题,请参考以下文章

带有 lambda 作为每个实例化的唯一默认参数的模板

python-clang:获取模板参数

带有非类型参数的奇怪模板实例化错误

显式实例化模板类的显式实例化模板方法

类和对象之模板

正确使用函数的显式模板实例化?