Общее хранение лямбд в структуре constexpr - PullRequest
0 голосов
/ 29 января 2020

У меня есть constexpr карта, которая связывает некоторый скалярный ключ со структурным значением constexpr, например,

struct Foo {
    constexpr Foo(int a_, char const *b_) : a(a_), b(b_) {}

    int a;
    char const *b;
};

constexpr const auto map = createMap<int, Foo>({
    {1, Foo{1234, "first"}},
    {2, Foo{5678, "second"}},
});

Я хотел бы добавить анонимную функцию к этой структуре, например,

template<typename FuncTy>
struct Foo {
    constexpr Foo(int a_, char const *b_, FuncTy c_) : a(a_), b(b_), c(c_) {}

    int a;
    char const *b;
    FuncTy c;
};

constexpr const auto map = createMap<int, Foo>({
    {1, Foo{1234, "first", []() {
        return -1;
    }}},
    {2, Foo{5678, "second", []() {
        return -2;
    }}},
});

Эти лямбды сами по себе не constexpr, но давайте предположим, что все они имеют одинаковую подпись и ничего не фиксируют. К сожалению, это не работает как есть, потому что FuncTy не может быть выведено. std::function не constexpr, поэтому его нельзя использовать. Точно так же определение FuncTy как типа другой лямбды с помощью decltype не работает.

Как определить пользовательские функции для каждой записи в этой карте, сохраняя при этом constexpr?

1 Ответ

1 голос
/ 29 января 2020

Во-первых, во втором примере Foo не класс; это шаблон. Шаблоны генерируют такие вещи, как классы, функции и т. Д. c, но сам шаблон не тот, который он генерирует. Так что, если эта createMap функция не принимает шаблон, а не специфицированный тип c, createMap<int, Foo> - нонсенс для вашего второго случая.

Если вы создаете однородный контейнер, то каждое значение, которое хранит ваш контейнер должен быть того же типа. Таким образом, единственный способ для этого состоит в том, чтобы принудительно установить определенный тип c, либо тот, который стирает тип данной лямбды, либо тот, который применяет указанную сигнатуру c функции. Например, взяв указатель на функцию:

using FuncTySignature = int(); //Insert signature here.
using FuncTyPtr = FuncTySignature*;

struct Foo {
    constexpr Foo(int a_, char const *b_, FuncTyPtr c_) : a(a_), b(b_), c(c_) {}

    int a;
    char const *b;
    FuncTyPtr c;
};

Теперь Foo больше не является шаблоном; он принимает указатель на функцию с заданной c сигнатурой.

Если вы должны заполнить эти функции лямбдами, а затем сделать их не захватывающими лямбда, используйте точную сигнатуру, указанную FuncTySignature, и преобразуйте лямбду в указатель на функцию при ее применении. Например:

constexpr const auto map = createMap<int, Foo>({
    {1, Foo{1234, "first", +[]() -> int {
        return -1;
    }}},
    {2, Foo{5678, "second", +[]() -> int {
        return -2;
    }}},
});

-> int просто для того, чтобы убедиться, что подпись совпадает. + перед лямбда-выражением используется для провоцирования преобразования лямбда-функтора в указатель на лямбда-функцию.

...