Constexpr конструктивная функция объекта - PullRequest
3 голосов
/ 05 марта 2020

У меня есть вопрос, несколько похожий на этот , но для более ограниченного случая, который, как я считаю, должен быть как-то возможен: я хочу построить массив вызовов функции statiti c constexpr из числа лямбды, каждая из которых имеет одну и ту же подпись. Часть stati c и constexpr здесь важна, так как я нахожусь во встроенной системе, где я хочу убедиться, что такие таблицы заканчиваются на Fla sh.

Так что в основном я хочу сделать это

#include<vector>
#include<functional>
#include<variant>

using params_t = std::vector<std::variant<int, float /*maybe others*/ >>;

struct command_t {
   using callable_t = std::function<void(params_t)>;

  const callable_t func;
   //other members..
};

class AClass {
    template<typename func_t>
    constexpr static command_t::callable_t make_callable(func_t fun) {
         return [fun](params_t params){/*construct a call to fun using params and template magic*/};
    }

    static void mycommand();
    static void mycommand2(int i);

    //The following fails: 
    ///"error: in-class initialization of static data member 'const command_t AClass::commands [2]' of non-literal type"
    static constexpr command_t commands[2] = {command_t{make_callable(mycommand)},
                                              command_t{make_callable(mycommand2)}};
};

На coliru

Обратите внимание, что тип стирания здесь весьма ограничен, поскольку сигнатура лямбды изменяется только сигнатурой захвата fun. Вызов функции, очевидно, не должен (и не может) быть constexpr, только конструкция.

Так что в основном мой вопрос заключается в том, могу ли я каким-то образом создать массив commands static constexpr, либо каким-либо образом используя std::function, или что-то вроде inplace_function , или, возможно, вращая мой собственный код для стирания лямбда-типа в этом конкретном c случае?

Ответы [ 2 ]

2 голосов
/ 05 марта 2020

В общем, это невозможно, так как захват лямбды может стать произвольно большим, и, следовательно, в какой-то момент нам нужно выделить кучу, которая затем убивает любые надежды на constexpr pre-C ++ 20 (а я не думаю, что C ++ 20 тоже очень поможет в этом случае).

Но вы хотите захватить указатель на функцию только в том случае, если я правильно понял и что мы можем сделать:

#include <vector>
 #include<variant>

using params_t = std::vector<std::variant<int, float /*maybe others*/ >>;

struct command_t {
   using callable_t = void (*)(std::vector<params_t>); 

  const callable_t func;
   //other members..
};

 template<auto f> 
 void wrap(std::vector<params_t>){
    // make this dependent of f, maybe use function_traits for fancy stuff
 }
class AClass {

    static void mycommand();
    static void mycommand2(int i);

    static constexpr command_t commands[2] = {wrap<mycommand>, wrap<mycommand2>};
};

 int main() {

 }

Спасибо xskxzr за ценные предложения.

0 голосов
/ 05 марта 2020

Поскольку mycommanN имеют разные подписи и вам нужно их захватить, я не вижу способа получить вектор constexpr. Может быть, кто-то может придумать лучший дизайн.

У меня есть решение: используйте std::tuple. Но мне не очень нравится, так как работать с tuple в качестве контейнера очень сложно. Например, повторение этого ... скажем, не прогулка в парке. Во всяком случае, вот в случае, если это поможет:

using params_t = std::vector<std::variant<int, float /*maybe others*/>>;

// I needed to lift this out of AClass because of ... complicated reasons
// (short version: when both are AClass members
//    the return type of `make_command` is not resolved in the init of `commands`
//    because both are static, `commands` is not a template and `make_command` is a template
//    tbh I don't know exactly what is happening. It's one of those dark corners of C++)

template <class RealFunc>
static constexpr auto make_command(RealFunc real_func) {
  return [real_func](params_t params) { /*magic*/ };
}

struct AClass {
  static void mycommand();
  static void mycommand2(int i);

  static constexpr std::tuple commands{make_command(mycommand),
                                       make_command(mycommand2)};
};

// usage
auto test() {
  constexpr auto command0 = std::get<0>(AClass::commands<>);

  params_t params0 = {};

  return command0(command0);
}
...