Почему инициализатор 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. Легко как то.

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