Невозможно создать класс из std::function при использовании внутри std::array

Я хочу иметь std:array из std::function, но я хочу убедиться, что все элементы массива инициализированы. Для этого я создал класс-оболочку, который принимает std::function в качестве параметра построения.

Но когда я инициализирую массив своего класса-оболочки напрямую своей функцией (той, которая должна быть внутри std::function), он не компилируется.

Вот проблема, дистиллированная:

#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'
};

Я попытался изменить массив на std::vector<C> (хотя это противоречит всей моей цели использования std:array для начала), и он отказывается компилировать любую из вышеперечисленных инициализаций.


person Equilibrius    schedule 30.10.2019    source источник
comment
Возможно, актуально: множественные неявные преобразования для пользовательских типов запрещены?.   -  person Daniel Langr    schedule 30.10.2019


Ответы (1)


В отличие от C c = f; (который представляет собой прямую инициализацию), в агрегатная инициализация, каждый элемент копирование инициализировано.

Каждый элемент массива direct public base, (since C++17) или нестатический член класса в порядке индекса массива/внешнего вида в определении класса copy-initialized из соответствующего пункта списка инициализаторов.

Это означает, что последний элемент wrapper, имеющий тип C, инициализируется копированием из f; который требует двух неявных преобразований. Преобразование указателя функции в F и преобразование из F в C. Оба они являются преобразованиями, определяемыми пользователем, но в одной последовательности неявных преобразований допускается только одно определяемое пользователем преобразование.

По той же причине C c = f; тоже не работает.

Вы можете добавить явное преобразование. например

static const std::array<C,Count> wrapper
{
    F{f},   //OK
    C{f},   //OK
    {f},    //OK
    static_cast<F>(f)
};

static const C c {f}; работает, потому что это прямая инициализация и ведет себя иначе с инициализация копирования. Для прямой инициализации будут рассмотрены конструкторы C, и один из них ожидает F в качестве параметра, f можно преобразовать в F, тогда все в порядке; здесь требуется только одно определяемое пользователем преобразование.

(выделено мной)

Кроме того, неявное преобразование при инициализации копированием должно генерировать T непосредственно из инициализатора, в то время как, например, прямая инициализация предполагает неявное преобразование инициализатора в аргумент конструктора T.

person songyuanyao    schedule 30.10.2019