Копирование параметра вызывает удаленный конструктор, когда этот конструктор не должен вызываться

#include <memory>

template <typename T>
class Wrapper {
public:
    Wrapper() = delete;
    Wrapper(const Wrapper&) = delete;
    Wrapper(Wrapper&&) = delete;

    ~Wrapper() = default;

    Wrapper(const T&) = delete;
    Wrapper(T&& in) : instance{std::move(in)} {}

    T instance;
};

void foo(Wrapper<std::shared_ptr<int>>) {}

int main() {
    auto ptr = std::make_shared<int>(1);
    foo(std::move(ptr));
}

Это работало в C++17, поэтому я никогда не задумывался над этим, но почему этот код пытается вызвать конструктор перемещения в C++14? Разве это не должно быть построено на месте в аргументе функции? Кажется, это не проблема с C++17, но не компилируется с C++14.

Единственный обходной путь, который я вижу, это сделать foo Параметр rvalue, но есть ли что-то, что я могу сделать, чтобы сделать эту работу без параметра в foo значение в C++14?


Моей первой мыслью было бы, что временный объект должен быть конструктором для передачи в функцию, но что еще более удивительно, это то, что даже при -fno-elide-constructors и удаление конструкторов перемещения и конструкторов копирования, которые, кажется, не называются! Это ошибка в gcc и clang?

См. https://wandbox.org/permlink/f6sa5Rm3NxZLy5P1 об ошибке и просмотрите странное поведение https://wandbox.org/permlink/Kh6CG4OVbUAjvEZz

1 ответ

Решение

Когда вы звоните foo(std::move(ptr)); Вы не даете это Wrapper<std::shared_ptr<int>>, Таким образом, компилятор генерирует временный и использует его для создания fooпараметр. Теперь это может быть исключено, и мы можем непосредственно построить Wrapper<std::shared_ptr<int>> но конструктор перемещения / копирования все еще должен быть доступен, даже если он никогда не вызывается.

С C++17 этого больше не происходит. Мы гарантировали исключение копирования, что означает, что временные данные никогда не материализуются, а параметр создается напрямую.

Другие вопросы по тегам