Неопределенная ссылка для ~queue с явным созданием экземпляра шаблона с помощью Clang 10

Следующий код не связывает 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;
}

Что я получаю от компилятора:

Онлайн-пример

/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++?


person PJK136    schedule 25.08.2020    source источник


Ответы (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, здесь есть ТАК: Почему я не могу создать очередь/стек со списками инициализаторов, заключенными в фигурные скобки? (С++ 11)

Но, похоже, вы можете скомпилировать свой код (по крайней мере, на coliru, я безуспешно пытался использовать godbolt), если вы используете libc++ (-stdlib=libc++): https://coliru.stacked-crooked.com/a/df9d859a239843cf

Возможно, это не даст точного ответа на ваш вопрос, но я был слишком длинным для комментария. Также вы можете найти аналогичную тему здесь: https://github.com/envoyproxy/envoy/issues/ 9106

[править] Интересно, после сброса пользовательского интерфейса godbolt и повторного ввода кода с той же конфигурацией (https://godbolt.org/z/TzE9h9) все работает нормально.

person marcinj    schedule 25.08.2020
comment
Я не думаю, что здесь проблема в инициализации списка для очереди, потому что мои фигурные скобки пусты. Они должны неявно преобразовывать их в std::queue, что разрешено, начиная с C++11, в соответствии с cppreference. Но я думаю, что компилятор Coliru Clang устарел, потому что std::queue конструктор по умолчанию отмечен как явный здесь, вот почему мой исходный пример не удалось скомпилировать. - person PJK136; 27.08.2020
comment
@ PJK136 Да, возможно, это так. godbolg показывает, что operator=(std::queue<int, std::deque<int, std::allocator<int> > >&&) вызывается в строке q = {};. Видите ли вы причину, по которой godbolt.org/z/TzE9h9 компилируется, а ссылка на Godbolt — нет? - person marcinj; 27.08.2020
comment
Да, это потому, что в меню «Вывод» я установил флажок «Скомпилировать в двоичный файл», а в вашем примере это не так (что является поведением по умолчанию). Когда вы не проверяете это, я думаю, что компилятор ничего не связывает (это похоже на параметр -c), поэтому ошибки ссылки нет. Вы можете попробовать на этом простом примере: godbolt.org/z/v7xaEq - person PJK136; 27.08.2020
comment
Я думаю, что это проблема компилятора, который не генерирует деструктор для очереди, но позже использует его во время компоновки. Если вы создадите экземпляр std::queue<int> где-нибудь в верхней части файла, он будет связан, потому что будет сгенерирован деструктор. Возможно, это связано с обязательным удалением копии, добавленным в С++ 17, но это только мое предположение. - person marcinj; 27.08.2020
comment
Я вижу, что на самом деле это то, что изменилось в версии 10: Уважайте исключение копии С++ 17; ранее он генерировал вызовы деструктора для пропущенных временных объектов, в том числе в операторах инициализации и возврата. и Не генерируйте повторяющиеся вызовы деструктора для выражений оператора. - person marcinj; 27.08.2020