我已经开始使用这种类型的构造,它依赖于 C++20 的 lambda 显式模板参数:
template<typename... Ts>
struct Foo
{
std::tuple<Ts...> bars;
auto get_labels(const std::array<std::size_t,sizeof...(Ts)>& indices) const
{
// construct tuple from bar labels
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return std::make_tuple(std::get<Is>(bars).get_label(indices[Is])...);
}(std::index_sequence_for<Ts...>{});
}
};
在 C++17 或 C++14 中是否有相对优雅的方法可以做到这一点?或者我现在应该将 C++20 作为一项要求?
3
最佳答案
4
因此,认清您真正需要的是哪个部分非常重要。当您写下以下内容时:
// construct tuple from bar labels
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return std::make_tuple(std::get<Is>(bars).get_label(indices[Is])...);
}(std::index_sequence_for<Ts...>{});
您实际需要的是Is...
。或者,更一般地说,您实际需要的是一组常量值。在上面的例子中,您接受一个参数index_sequence<Is...>
– 这是一个参数,其类型包含一组常量值。
但另一种方法是接受N
不同的参数,其中第一个参数是 类型integral_constant<size_t, 0>
,第二个参数是 类型integral_constant<size_t, 1>
,依此类推。如果你可以生成这些参数,那么 lambda 部分就变成了
[&](auto... Is){
return std::make_tuple(std::get<Is>(bars).get_label(indices[Is])...);
}
请注意,主体是相同的,我只是改变了参数的样子。现在这是一个有效的 C++14 lambda。
因此,问题的其余部分是生成函数模板with<N>(f)
,该模板调用f(integral_constant<size_t, 0>{}, integral_constant<size_t, 1>{},..., integral_constant<size_t, N-1>{})
允许您调用:
return with<sizeof...(Ts)>([&](auto... Is){
return std::make_tuple(std::get<Is>(bars).get_label(indices[Is])...);
})
而且with
用 C++14 编写很简单。这实际上只是同样的index_sequence
技巧,但有一个额外的间接寻址(因为您需要将 one 更改index_sequence
为N
integral_constant
s)。可以说,结果看起来也更好 – 它不那么繁忙。无论如何,我更喜欢 C++20 中的这个。
3
-
只是为了确保我没有遗漏任何东西;每个
Is
将是不同 Xauto...Is
的一个实例。然后中的将被转换为整数,因为有一个转换运算符是 constexpr。是这样吗?std::integral_constant<size_t,X>
Is
get<Is>
std::integral_constant
– -
@edrezen 是的,完全正确。
– -
好的,谢谢。你确实说服了我使用类似方案
with
来迭代整数范围,例如在为 提供参数的情况下std::get
。
–
|
一种可能的方法是将序列直接“放置”在类模板中:
template <typename Seq, typename... Ts> struct FooImpl;
template <std::size_t... Is, typename... Ts>
struct FooImpl<std::index_sequence<Is...>, Ts...>
{
static_assert(sizeof...(Is) == sizeof...(Ts));
std::tuple<Ts...> bars;
auto get_labels(const std::array<std::size_t, sizeof...(Ts)>& indices) const
{
// construct tuple from bar labels
return std::make_tuple(std::get<Is>(bars).get_label(indices[Is])...);
}
};
template <typename... Ts>
using Foo = FooImpl<std::make_index_sequence<sizeof...(Ts)>, Ts...>;
1
-
1@Barry 的答案可能最适合我实际提出的问题,但我认为这个答案是我想要的。这使我避免在类中立即调用一堆 lambda。而且我已经在
impl::
命名空间中实现了,所以这甚至不会改变我的接口。
–
|
一种直接的方法是将 lambda 变成成员函数:
template<typename... Ts>
struct Foo
{
std::tuple<Ts...> bars;
template <std::size_t... Is>
auto get_labels_(
const std::array<std::size_t, sizeof...(Ts)>& indices,
std::index_sequence<Is...>
) const
{
return std::make_tuple(std::get<Is>(bars).get_label(indices[Is])...);
}
auto get_labels(const std::array<std::size_t,sizeof...(Ts)>& indices) const
{
// construct tuple from bar labels
return get_labels_(indices, std::index_sequence_for<Ts...>{});
}
};
但不确定它是否算得上相对优雅。
1
-
嗯,我想这可行。这实际上会使我的类的长度增加一倍,所以这不是很优雅;至少我可以将实现函数隐藏为私有函数,我想。
–
|
解决缺少模板 lambda 问题的一种方法是使用返回 lambda 的模板方法:
template<typename... Ts>
struct Foo
{
std::tuple<Ts...> bars;
template <std::size_t... Is>
auto make_lambda (std::index_sequence<Is...>, const std::array<std::size_t,sizeof...(Ts)>& indices) const
{
return [&] (std::index_sequence<Is...>)
{
return std::make_tuple(std::get<Is>(bars).get_label(indices[Is])...);
};
}
auto get_labels (const std::array<std::size_t,sizeof...(Ts)>& indices) const
{
auto seq = std::index_sequence_for<Ts...>{};
return make_lambda (seq, indices) (seq);
}
};
这应该从 c++14 开始起作用。
注意:这在某种程度上类似于我在发布之前没有见过的先前答案。也许它更强调了潜在的意图,即制作 lambda 模板,但更加不友好。
因此,如果您可以在自己的环境中使用 c++20,那么它是一个很好的举措。
|
–
–
–
|