Маркировка функции как виртуальной вызывает ошибку компилятора с unique_ptr

У меня есть шаблонный класс, который переносит вектор. Я пытаюсь сохранить 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;
}

7

Решение

Проблема здесь в том, что 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, потому что ошибка не возникает во время подстановки выводимых аргументов.

Заключение: это не ошибка но качество исполнения вопрос.

5

Другие решения

Скорее всего, это связано с тем, что ContainerWrapper является шаблоном. С шаблонами компилятор чаще всего даже не проверяет функции-члены, если вы их не вызываете. Однако, отметив его виртуальным, функция должна присутствовать (вы также можете получить ошибку ссылки).

Вы можете взглянуть на этот пост: Когда создаются виртуальные функции-члены шаблонного класса?.

9

По вопросам рекламы [email protected]