Why the initializer of std::function has to be CopyConstructible?

前端 未结 3 1289
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-07 01:37

According to http://en.cppreference.com/w/cpp/utility/functional/function/function, the type of the initializer, i.e., F in form (5), should meet the requiremen

相关标签:
3条回答
  • 2020-12-07 02:06

    std::function is CopyConstructible (see Constructor (3) in the documentation). You can only copy an object if all its components can be copied. So the contained F must be CopyConstructible as well. Easy as that.

    0 讨论(0)
  • 2020-12-07 02:20

    std::function uses type erasure internally, so F has to be CopyConstructible even if the particular std::function object you are using is never copied.

    A simplification on how type erasure works:

    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()) {}
        //...
    
    };
    

    You have to be able to generate Model<F>::clone(), which forces F to be CopyConstructible.

    0 讨论(0)
  • 2020-12-07 02:32

    The example from @Nevin is illuminating in terms of showing an implementation option. Still, there is something more fundamental at work here. It is not an artifact of the particular implementation technique used.

    In particular, the virtual is not really the key here. Consider this alternative implementation that does not use virtual (other than at the destructor).

    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; }
        //...
    };
    

    see http://ideone.com/FKFktK for the full version and example output

    Consider what is the value of the expression (also in http://ideone.com/FKFktK):

    is_copy_constructible<function<void()>>::value

    The answer cannot depend on the properties of specific instances or how they were constructed, there isn't even an instance to look at in this case. Copyability is a property of a type, not of an instance. So the answer has to be uniformly true or false across all instances.

    The standard chose is_copy_constructible<function<void()>>::value to be true. As a consequence, the standard is forced to require that is_copy_constructible<F>::value also be true regardless of implementation internals of the std::function template.

    If we chose is_copy_constructible<function<void()>>::value to be false, then no instance would be copyable regardless of whether some specific F itself was copyable.

    0 讨论(0)
提交回复
热议问题