模板类型(静态)包装函数?

Template type to (statically) wrap a function?

提问人:BCS 提问时间:12/3/2022 最后编辑:BCS 更新时间:12/3/2022 访问量:61

问:

C++ 标准是否定义了将函数包装在类型中的实用程序(与将其包装在值中不同)?

在谷歌上搜索了一堆看起来相关的名字后,我什么也没找到,但话又说回来,我知道一些我永远不会猜到名字的事情。


编辑:

  • 我已经知道如何获得一个可以动态接受函数值的类型(或者一个好的旧函数指针,如 ),但它特别从类型中排除了我最想包含的一件事:要调用的实际函数。std::functionint(*f)(int)
  • 我已经知道如何从函数的名称 () 中获取函数的类型,这不是我想要的,原因与上面列出的相同。decltype(fn)

我发现自己需要创建一个与函数完全匹配的类型(在我的情况下,来自第 3 部分库的 C 函数)。理想情况下,该类型在任何时候使用时都应该内联为零。作为一次性的,这很容易:operator()foo

struct foo_t {
  ret_t operator()(SomeType t, int i) {
    return foo(t, i);
  }
};

但是,至少在一种情况下,需要对一堆不同的函数执行此操作:删除器,同时与不透明句柄(例如来自 C 库)交互:std::unique_ptr<T,D>

std::unique_ptr<FILE*, fclose_t> f(fopen(n, "r"));
std::unique_ptr<obj*, free_t> o((obj*)malloc(sizeof(obj)));
...

与其自己定义 、 等,不如说我想做的是这样的:fclose_tfree_t

std::unique_ptr<FILE*, type_fn<fclose>> f(fopen(n, "r"));
std::unique_ptr<obj*, type_fn<free>> o((obj*)malloc(sizeof(obj)));
...

这还不难......如果您乐于定义自己:type_fn

template<auto *f> struct fn_type {
  template<class... A>
  auto operator() (A&&... a) {
    return f(std::forward<A>(a)...);
  }
};

这让我回到了开头的问题:C++是否定义了这样的东西?

C++ 模板 std

评论

1赞 van con Nguyen 12/3/2022
看来您正在寻找关键字。decltype
0赞 BCS 12/3/2022
@vanconNguyen IIRC 抛弃了除调用签名之外的所有内容(这里只是偶然感兴趣的)。那么,你怎么能从一个调用签名变成一个正确的函数(在众多函数中)来自该签名呢?您不能调用调用签名,只能调用具有调用签名的内容。decltype
0赞 Ranoiaetep 12/3/2022
“这还没那么难......” -- 实际上,我可以获取标准库中定义的函数的地址吗?
0赞 BCS 12/4/2022
@Ranoiaetep FWIW,挑衅案例(我预计大多数潜在应用程序)都会调用第三方函数。我使用函数重写了它们,以避免描述不相关的细节。-- 也就是说,lambda 确实解决了重载函数不起作用的问题......代价是失去与 C++17 的兼容性并返回。std::

答:

0赞 Ranoiaetep 12/3/2022 #1

这甚至不是那么难......如果你乐于定义自己......type_fn

实际上,您不应该从标准库中获取函数的地址(有一些例外),因为库可能会对函数进行与函数的正常使用兼容的更改,但不会与获取其地址兼容(例如,添加具有默认值的参数,或向重载集添加重载)。

事实上,从 C++20 开始,明确禁止从标准库中获取函数的地址。更多信息可以在这里找到:我可以获取标准库中定义的函数的地址吗?

这意味着你不能写类似 .type_fn<fclose>


您的解决方案是使用 lambda,因为每个 lambda 的类型保证是唯一的:

auto deleter = [](auto *p){ std::fclose(p); };

现在,您可以在任何需要为其指定类型的位置使用:decltype(deleter)

auto f = std::uniqe_ptr<std::FILE, decltype(deleter)>(std::fopen("file", "r"));

如果您只需要一次此删除器,则还可以在模板参数中定义 lambda:

std::uniqe_ptr<std::FILE, decltype(
    [](auto *p){ std::fclose(p); }
)>(std::fopen("file", "r"));

评论

0赞 BCS 12/4/2022
从C++11到C++17,最后一种形式是非法的:N4659 8.1.5(2)。即使是第一种形式在 C++20 N4659 8.1.5.1 (11) 之前也不起作用——C++20 似乎消除了这两个限制。强制 >=C++20 是不可预测的。A lambda-expression shall not appear in an unevaluated operand (Clause 8), in a template-argument, in an alias-declaration, in a typedef declaration, [...]The closure type associated with a lambda-expression has no default constructor