У меня есть шаблонный класс, который переносит вектор. Я пытаюсь сохранить unique_ptrs в этом классе, и он отлично работает. Тем не менее, когда я отмечаю void add(const T& elem)
В качестве виртуальной функции мой компилятор (clang) сообщает мне, что я делаю «вызов неявно удаленного конструктора копии» для unique_ptr.
Я понимаю, что unique_ptrs нельзя скопировать, поэтому я создал void add(T&& elem)
функция. Я просто не знаю, почему пометка другой функции add как виртуальной вызывает ошибку компилятора.
Спасибо за ваше время.
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
template <typename T>
class ContainerWrapper {
private:
vector<T> vec;
public:
ContainerWrapper() : vec() {
}
//Marking this as virtual causes a compiler error
void add(const T& elem) {
vec.push_back(elem);
}
void add(T&& elem) {
vec.push_back(std::move(elem));
}
T removeLast() {
T last = std::move(vec.back());
vec.pop_back();
return last;
}
};
int main() {
ContainerWrapper<unique_ptr<string>> w;
w.add(unique_ptr<string>(new string("hello")));
unique_ptr<string> s = w.removeLast();
cout << *s << endl;
}
Проблема здесь в том, что std::unique_ptr
имеет конструктор копирования, помеченный как =delete
, Это означает, что vec.push_back(elem)
позвоните внутрь add(T const&)
перегруженная функция-член не будет компилироваться при вызове с std::unique_ptr
, Компилятор поймет это, как только будет создан экземпляр этой функции-члена.
стандарт имеет 2 соответствующие цитаты здесь, в 14.7.1 Неявная реализация [temp.inst]:
6 Если процесс разрешения перегрузки может определить правильный
функция, вызываемая без создания определения шаблона класса,
не указано, имеет ли место эта реализация на самом деле.10 […] Не определено, является ли реализация неявной или нет
создает виртуальную функцию-член шаблона класса, если
Виртуальная функция-член иначе не была бы создана. […]
Статья 6 гласит, что без virtual
ключевое слово — компилятор разрешен, но не обязателен для создания экземпляров обоих add(T const&)
а также add(T&&)
чтобы решить, какая перегрузка является лучшим соответствием. Ни gcc 4.7.2, ни Clang 3.2 не нуждаются в создании экземпляров, поскольку они выводят, что ссылки на rvalue всегда лучше соответствуют временным ссылкам, чем ссылки на lvalue.
Статья 10 гласит, что даже с virtual
ключевое слово — компилятор также разрешен, но не обязателен для создания экземпляра add(T const&)
а также add(T&&)
чтобы решить, какая перегрузка является лучшим соответствием. И gcc 4.7.2, и Clang 3.2 происходят с экземплярами обеих функций-членов, хотя они оба могли бы сделать вывод, что перегрузка lvalue никогда не будет лучшим совпадением.
Обратите внимание, что если вы делаете ContainerWrapper
регулярный класс с вложенным typedef unique_ptr<string> T;
, тогда и gcc, и Clang будут генерировать ошибки с или без virtual
ключевое слово, потому что они должны генерировать код для обеих функций-членов. Это не будет удалено SFINAE, потому что ошибка не возникает во время подстановки выводимых аргументов.
Заключение: это не ошибка но качество исполнения вопрос.
Скорее всего, это связано с тем, что ContainerWrapper является шаблоном. С шаблонами компилятор чаще всего даже не проверяет функции-члены, если вы их не вызываете. Однако, отметив его виртуальным, функция должна присутствовать (вы также можете получить ошибку ссылки).
Вы можете взглянуть на этот пост: Когда создаются виртуальные функции-члены шаблонного класса?.