#c #copy-constructor #move-constructor #function-object
Вопрос:
Согласно http://en.cppreference.com/w/cpp/utility/functional/function/function, тип инициализатора, т. е. F
в форме (5), должен соответствовать требованиям CopyConstructible. Я не совсем понимаю это. Почему это не нормально для F
того, чтобы быть просто подвижным?
Ответ №1:
std::функция использует стирание типов внутри, поэтому F должен быть копируемым, даже если конкретный объект std::function, который вы используете, никогда не копируется.
Упрощение того, как работает стирание типов:
class Function
{
struct Concept {
virtual ~Concept() = defau<
virtual Concept* clone() const = 0;
//...
}
template<typename F>
struct Model final : Concept {
explicit Model(F f) : data(std::move(f)) {}
Model* clone() const override { return new Model(*this); }
//...
F data;
};
std::unique_ptr<Concept> object;
public:
template<typename F>
explicit Function(F f) : object(new Model<F>(std::move(f))) {}
Function(Function constamp; that) : object(that.object->clone()) {}
//...
};
Вы должны уметь генерировать Model<F>::clone()
, что заставляет F быть копируемым.
Комментарии:
1. Итак, ключ кроется в
virtual
ключевом слове. Виртуальный метод всегда создается, даже если он является членом шаблона класса и фактически не используется. Напротив, невиртуальный метод шаблона класса создается только тогда, когда он фактически используется. Похоже, что если я уверенfunction<F>
, что не буду скопирован, тогда безопасно реализовать конструктор копированияF
как фиктивный, так как он никогда не будет вызван на самом деле.2. @Lingxi: Тщательно документируйте это, и у вас есть утверждения и исключения.
Ответ №2:
Пример из @Nevin показателен с точки зрения демонстрации варианта реализации. Тем не менее, здесь действует нечто более фундаментальное. Это не является артефактом конкретной используемой техники реализации.
В частности, на самом virtual
деле это не является ключевым моментом здесь. Рассмотрим эту альтернативную реализацию, которая не используется virtual
(кроме как в деструкторе).
class Function
{
struct Concept {
typedef Concept * (*cloneFunType)(const Concept *);
cloneFunType m_cloneFun = nullptr;
virtual ~Concept() = defau<
};
template<typename F> struct Model final : Concept {
static Concept* clone(const Concept *c) {
return new Model(static_cast<const Model*>(c)->data); }
explicit Model(F amp;amp;f) : data(move(f)) { this->m_cloneFun = amp;Model::clone;}
explicit Model(const F amp;f) : data(f) { this->m_cloneFun = amp;Model::clone; }
F data;
};
Concept* object;
public:
~Function() { delete object; }
template<typename F> explicit Function(Famp;amp; f)
: object(new Model<typename remove_reference<F>::type>(forward<F>(f))) {}
Function(Function constamp; that)
: object((*(that.object->m_cloneFun))(that.object)) {}
Function(Function amp;amp; that) : object(that.object) { that.object = nullptr; }
//...
};
видишь http://ideone.com/FKFktK для полной версии и вывода примера
Рассмотрим, каково значение выражения (также в http://ideone.com/FKFktK):
is_copy_constructible<function<void()>>::value
Ответ не может зависеть от свойств конкретных экземпляров или от того, как они были созданы, в этом случае даже нет экземпляра, на который можно было бы посмотреть. Копируемость-это свойство типа, а не экземпляра. Таким образом, ответ должен быть одинаковым true
или false
во всех случаях.
Стандарт решил is_copy_constructible<function<void()>>::value
быть true
таким . Как следствие, стандарт вынужден требовать, чтобы это is_copy_constructible<F>::value
также было true
независимо от внутренних компонентов реализации шаблона функции std::.
Если бы мы выбрали is_copy_constructible<function<void()>>::value
это false
, то ни один экземпляр не был бы копируемым, независимо от того, был ли какой-то конкретный F
экземпляр копируемым.
Ответ №3:
std::function
является копируемым (см. Конструктор (3) в документации). Вы можете скопировать объект только в том случае, если все его компоненты могут быть скопированы. Таким образом, содержащееся F
также должно быть копируемым. Вот так просто.
Комментарии:
1. Значит, если бы этот конструктор был удален, проблема исчезла бы так просто? 🙂