c++ - 我可以更改通用可变参数 lambda 的模板参数推导顺序吗?
问题描述
以下面的代码为例,这是一个简化的例子:
template <typename F>
void foo(F f) {
//bool some = is_variadic_v<F>; // Scenario #1
bool some = true; // Scenario #2
f(int(some), int(some));
}
int main() {
auto some = [](int i, int j) {
std::cout << i << " " << j << '\n';
};
foo([&some](auto... params) {
some(params...);
});
}
一个函数接受一个通用的可变参数 lambda 并使用一组固定的参数调用它。然后这个 lambda 本身只是调用另一个具有匹配原型的函数/lambda。可以预料,在场景 2 中,当f
被调用 inside时foo
,编译器将推断params...
为参数 pack {1, 1}
。
对于场景 #1,我使用另一个问答中的代码来推断可调用对象的数量。但是,如果这样的对象可以使用超过预定义的最大参数数量进行调用,则将其视为“可变参数”。详细地说,is_variadic_v
将使用表达式 SFINAE 的一种形式,它试图调用具有可隐式转换为任何东西的“任意类型”的参数数量减少的函数对象。
现在的问题是,显然,编译器将在此元代码期间推导出 F(及其参数包),如果它是可变参数(例如在本例中),它将 F 推导出为采用虚拟参数的 lambda,即类似于main()::lambda(<arbitrary_type<0>, arbitrary_type<1>, arbitrary_type<2>, ..., arbitrary_type<N>>)
如果 N 是上面的“可变限制”。现在params...
推断为arbitrary_type<1>, arbitrary_type<2>, ...
和相应地,调用some(params...)
将失败。这种行为可以在这个小代码示例中演示:
#include <utility>
#include <type_traits>
#include <iostream>
constexpr int max_arity = 12; // if a function takes more arguments than that, it will be considered variadic
struct variadic_type { };
// it is templated, to be able to create a
// "sequence" of arbitrary_t's of given size and
// hence, to 'simulate' an arbitrary function signature.
template <auto>
struct arbitrary_type {
// this type casts implicitly to anything,
// thus, it can represent an arbitrary type.
template <typename T>
operator T&&();
template <typename T>
operator T&();
};
template <
typename F, auto ...Ints,
typename = decltype(std::declval<F>()(arbitrary_type<Ints>{ }...))
>
constexpr auto test_signature(std::index_sequence<Ints...> s) {
return std::integral_constant<int, size(s)>{ };
}
template <auto I, typename F>
constexpr auto arity_impl(int) -> decltype(test_signature<F>(std::make_index_sequence<I>{ })) {
return { };
}
template <auto I, typename F, typename = std::enable_if_t<(I > 0)>>
constexpr auto arity_impl(...) {
// try the int overload which will only work,
// if F takes I-1 arguments. Otherwise this
// overload will be selected and we'll try it
// with one element less.
return arity_impl<I - 1, F>(0);
}
template <typename F, auto MaxArity>
constexpr auto arity_impl() {
// start checking function signatures with max_arity + 1 elements
constexpr auto tmp = arity_impl<MaxArity+1, F>(0);
if constexpr (tmp == MaxArity+1)
return variadic_type{ }; // if that works, F is considered variadic
else return tmp; // if not, tmp will be the correct arity of F
}
template <typename F, auto MaxArity = max_arity>
constexpr auto arity(F&&) { return arity_impl<std::decay_t<F>, MaxArity>(); }
template <typename F, auto MaxArity = max_arity>
constexpr auto arity_v = arity_impl<std::decay_t<F>, MaxArity>();
template <typename F, auto MaxArity = max_arity>
constexpr bool is_variadic_v = std::is_same_v<std::decay_t<decltype(arity_v<F, MaxArity>)>, variadic_type>;
template <typename F>
void foo(F f) {
bool some = is_variadic_v<F>;
//bool some = true;
f(int(some), int(some));
}
int main() {
auto some = [](int i, int j) {
std::cout << i << " " << j << '\n';
};
foo([&some](auto... params) {
some(params...);
});
}
我可以防止这种行为吗?我可以强制编译器重新推导参数列表吗?
编辑:
另一个特点是编译器似乎表现得有点精神分裂。当我将内容更改foo
为
foo([&some](auto... params) {
// int foo = std::index_sequence<sizeof...(params)>{ };
std::cout << sizeof...(params) << '\n';
});
编译器将创建一个将2
在此示例中打印的程序。但是,如果我包含注释行(这没有意义,应该触发编译器诊断),我会遇到
error: cannot convert 'std::index_sequence<13>' {aka 'std::integer_sequence<long unsigned int, 13>'} to 'int' in initialization
85 | int foo = std::index_sequence<sizeof...(params)>{ };
那么编译器现在是否同时推断sizeof...(params)
为是2
和13
?或者他改变主意并选择现在13
只是因为我在 lambda 中添加了另一个语句?如果我改为选择static_assert(2 == sizeof...(params));
. 所以编译器推断sizeof...(params) == 2
,除非我问他是否做了推断2
,因为那时他没有。
显然,lambda里面写的是什么,对于参数包推导是非常有决定性的。只是我还是这种行为真的看起来很病态?
解决方案
推荐阅读
- numpy - 是否有一种矢量化的方法来创建一个矩阵,其中每个元素都是矩阵的逐行点积?
- python - 当我运行我的网站时,背景图像上出现 404 错误
- r - 查找一列的(5)最大值并根据其他列的不同值与最小值合并在一起的函数
- c# - 使用 Elasticsearch 按 ID 搜索不返回
- smtp - PMTA 到 PMTA 路由更改电子邮件标题
- apache-spark - Spark结构化流中的自定义窗口
- python - 通过 Scrapy 上的多个链接爬行
- python - 如果有人单击反应更改消息并删除反应 discord.py,则机器人不和谐
- csv - 发布工作表时如何仅显示过滤后的数据?
- python - 如何根据多列中的多个条件计算/求和值