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 requirements of CopyConstructible. I don't quite get this. Why is it not OK for F
to be just MoveConstructible?

- 64,318
- 19
- 100
- 158

- 14,579
- 2
- 37
- 93
-
1despite existing answers, it can as well throw exception when requested. just like the `std::any` or `std::variant`. so it's indeed a specification/design artifact. – apple apple Jan 11 '23 at 16:28
3 Answers
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.

- 4,595
- 18
- 24
-
So, the key lies in the `virtual` keyword. A virtual method is always instantiated even if it is a member of a class template and is not actually used. On the contrary, a non-virtual method of a class template is only instantiated when it is actually used. It seems that if I'm sure `function
` will not be copied, it is then safe to implement the copy constructor of `F` as a dummy, as it will never be actually called. – Lingxi Jul 10 '14 at 03:26 -
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.

- 2,835
- 2
- 22
- 35
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.

- 38,535
- 21
- 92
- 152
-
So if that constructor were removed, the problem would go away, Easy as that? :-) – Don Hatch Mar 07 '21 at 00:12