Почему инициализатор std::function должен быть CopyConstructible?
Согласно http://en.cppreference.com/w/cpp/utility/functional/function/function, типу инициализатора, т.е. F
в форме (5) должен соответствовать требованиям CopyConstructible. Я не совсем понимаю это. Почему это не хорошо для F
быть просто MoveConstructible?
3 ответа
std::function использует тип erasure для внутреннего использования, поэтому F должен быть CopyConstructible, даже если конкретный используемый вами объект std::function никогда не копируется.
Упрощение того, как работает стирание типов:
class Function
{
struct Concept {
virtual ~Concept() = default;
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 const& that) : object(that.object->clone()) {}
//...
};
Вы должны быть в состоянии генерировать Model<F>::clone()
, что заставляет F быть CopyConstructible.
Пример от @Nevin освещает с точки зрения показа варианта реализации. Тем не менее, здесь есть что-то более фундаментальное. Это не артефакт конкретной используемой техники реализации.
В частности, virtual
здесь не совсем ключ. Рассмотрим эту альтернативную реализацию, которая не использует virtual
(кроме деструктора).
class Function
{
struct Concept {
typedef Concept * (*cloneFunType)(const Concept *);
cloneFunType m_cloneFun = nullptr;
virtual ~Concept() = default;
};
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 &&f) : data(move(f)) { this->m_cloneFun = &Model::clone;}
explicit Model(const F &f) : data(f) { this->m_cloneFun = &Model::clone; }
F data;
};
Concept* object;
public:
~Function() { delete object; }
template<typename F> explicit Function(F&& f)
: object(new Model<typename remove_reference<F>::type>(forward<F>(f))) {}
Function(Function const& that)
: object((*(that.object->m_cloneFun))(that.object)) {}
Function(Function && 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::function.
Если мы выбрали is_copy_constructible<function<void()>>::value
быть false
, то ни один экземпляр не будет копируемым независимо от того, F
сам был копируемый.
std::function
является CopyConstructible (см. Constructor (3) в документации). Вы можете скопировать объект, только если все его компоненты могут быть скопированы. Таким образом, содержится F
также должен быть CopyConstructible. Легко как то.