【问题标题】:Constexpr constructible function objectconstexpr 可构造函数对象
【发布时间】:2023-03-25 02:45:01
【问题描述】:

我有一个与this one 有点相似的问题,但对于一个更有限的情况,我认为这应该是可能的:我想从多个 lambda 构造一个静态 constexpr 函数调用数组,每个 lambda 共享相同的签名.静态和 constexpr 部分在这里很重要,因为我在嵌入式系统上,我想确保这些表最终出现在 Flash 中。

所以基本上我想做的是

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

On coliru

请注意,这里的类型擦除非常有限,因为 lambda 的签名仅因捕获 fun 的签名而异。函数调用显然不需要(也不能)是 constexpr,只需构造。

所以基本上我的问题是我能否以某种方式制作commands 数组static constexpr,或者以某种方式使用std::function,inplace_function 之类的东西,或者可能通过旋转我自己的代码来对特定类型的lambda 进行类型擦除案例?

【问题讨论】:

  • 没有捕获的Lambas可以转换为标准函数指针。这也意味着您可以摆脱不是 constexpr 的 std::function 对象。所以你会有using callable_t = void (*)(params_t);。我不确定make_callable 应该做什么。
  • @Darhuuk 请注意,lambda 确实有捕获。 make_callable 实际上是一个模板:fun 可以有任意数量和类型的参数,make_callable 构造一个调用 fun 的 lambda,参数来自作为参数给出的变体向量(显然必须匹配fun 的签名,在运行时检查)。
  • 啊,我明白了。那么目标是“自动化”吗?因为在我看来,如果您手动写出每个 lambda,则无需捕获 fun(因为它们是静态函数)。
  • @Darhuuk 没错!

标签: c++ c++17 constexpr type-erasure std-function


【解决方案1】:

一般来说,这是不可能的,因为 lambda 的捕获可能会变得任意大,因此,在某些时候我们需要一个堆分配,这会扼杀 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 的宝贵建议。

【讨论】:

  • 看起来很有希望,我明天试试!我想你在调用 do_wrap 时不应该在函数名前加上&amp;
  • f被声明为函数参数时,在编译时无法知道,因此不能用作模板参数。这就是您的do_wrap 不起作用的原因。顺便说一句,为什么需要wrapper 类?直接声明template&lt;auto f&gt; void wrap(std::vector&lt;int&gt;);有问题吗?
  • auto f 是 C++20,我想我们想提取参数。
  • 是C++17,你也可以在decltype(f)上使用function_traits提取Args...
  • 啊,那我脑子有问题。是的,这简化了事情。我会修改答案。
【解决方案2】:

由于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);
}

【讨论】:

  • 元组不起作用,因为使用命令表的代码将取决于所有可能命令的类型,这就是我想要避免的。
猜你喜欢
  • 2022-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-21
  • 1970-01-01
  • 2017-10-31
  • 2013-10-20
相关资源
最近更新 更多