5

The following code doesn't link Clang 10 but succeed with GCC and 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;
}

What I get from the compiler is:

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

It works if I replace std::queue with std::vector, std::deque or std::set; or if I remove the explicit template instantiation.

It also works if I remplace q = {} with the full constructor call q = std::queue<int>{}.

Is this code not standard or is it a compiler/libc++ bug?

PJK136
  • 51
  • 3

1 Answers1

0

I am not sure why you get such linker error, maybe its some unique issue with godbolt. If you try to compile your code with coliru : https://coliru.stacked-crooked.com/a/ac9c188334f858d8 you will get a compiliation time error indicating that you try to use list initialization of your queue:

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.

queue does not allow list initialization with initializer_list, there is SO on this here: Why can't I construct a queue/stack with brace-enclosed initializer lists? (C++11)

But it appears you can make your code compile (at least on coliru, I tried on godbolt with no success) if you use libc++ (-stdlib=libc++) : https://coliru.stacked-crooked.com/a/df9d859a239843cf

It might not give exact answer to your question, but I it was too long for a comment. Also you might find similar thread here : https://github.com/envoyproxy/envoy/issues/9106

[edit] Interesting, after reseting godbolt UI and entering code again with the same configuration (https://godbolt.org/z/TzE9h9) everything works ok.

marcinj
  • 48,511
  • 9
  • 79
  • 100
  • I don't think that list initialization for queue is the problem here because my braces are empty. They should convert these implicitly into an `std::queue`, which is allowed since C++11 according to [cppreference](https://en.cppreference.com/w/cpp/container/queue/queue). But I think Coliru Clang compiler is outdated because `std::queue` default constructor is marked as explicit [here](https://coliru.stacked-crooked.com/a/601bebede8849753), that's why my original example failed to compile. – PJK136 Aug 27 '20 at 08:29
  • @PJK136 Yes, that might be it. godbolg shows that `operator=(std::queue > >&&)` is beeing called in line `q = {};`. Do you see a reason why https://godbolt.org/z/TzE9h9 compiles while your linked godbold link does not? – marcinj Aug 27 '20 at 09:12
  • Yes, it's because in the "Output" menu, I checked "Compile to binary" while, in your example, you don't (which is the default behavior). When you don't check this, I think the compiler don't link anything (it's like the "-c" option) so there is no link error. You can try on this simple example: https://godbolt.org/z/v7xaEq – PJK136 Aug 27 '20 at 10:13
  • I think its a problem w compiler which does not generate destructor for queue, but later it uses it at link time. If you instantiate `std::queue` somwhere at the top of the file, it will link because destructor will be generated. It might have to do with mandatory copy elision added in c++17, but that is only my guess. – marcinj Aug 27 '20 at 11:45
  • I see this is actually what the changed in 10 version: "Respect C++17 copy elision; previously it would generate destructor calls for elided temporaries, including in initialization and return statements." and "Don’t generate duplicate destructor calls for statement expressions." – marcinj Aug 27 '20 at 13:22