I want to have an std:array
of std::function
, but I want to make sure that all elements of the array are initialized.
For that end I built a wrapper class that takes an std::function
as a construction parameter.
But when I initialize an array of my wrapper class directly with my function (the one who should be inside std::function
) it fails to compile.
Here is the problem, distilled:
#include <functional>
#include <array>
static void f() {}
using F = std::function<void(void)>;
enum { Count = 4 };
struct C
{
//To get a compilation error when some
// elements of the array are not initialized.
C() = delete;
C(F) {}
};
//OK
static const C c {f};
//OK
static const std::array<F,Count> direct
{
F{f},
{f},
f,
f
};
static const std::array<C,Count> wrapper
{
F{f}, //OK
C{f}, //OK
{f}, //OK
f //could not convert 'f' from 'void()' to 'C'
};
I tried changing the array to an std::vector<C>
(although it defeats my whole purpose of using an std:array
to begin with) and it refuses to compile any of the above initializations.
Unlike C c = f;
(which is direct initialization), in aggregate initialization, every element is copy initialized.
Each
direct public base, (since C++17)
array element, or non-static class member, in order of array subscript/appearance in the class definition, is copy-initialized from the corresponding clause of the initializer list.
That means, the last element of wrapper
, which is of type C
, is copy-initialized from f
; which requiers two implicit conversions. The conversion from the function pointer to F
, and the conversion from F
to C
. Both of them are user-defined conversions, but only one user-defined conversion is allowd in one implicit conversion sequence.
For the same reason C c = f;
fails either.
You can add explicit conversion. e.g.
static const std::array<C,Count> wrapper
{
F{f}, //OK
C{f}, //OK
{f}, //OK
static_cast<F>(f)
};
static const C c {f};
works because it's direct initialization and behaves differently with copy initialization. For direct initialization the constructors of C
will be considered and one of them expects an F
as parameter, f
could be converted to F
then anything is fine; only one user-defined conversion is required here.
(emphasis mine)
In addition, the implicit conversion in copy-initialization must produce T directly from the initializer, while, e.g. direct-initialization expects an implicit conversion from the initializer to an argument of T's constructor.