[英]Python-like dynamic argument unpacking in C++?
I have a std::map<int, std::vector<MyClass>>
and a function that accepts a dynamic number of iterators, specifically iter::zip_longest
. 我有一个
std::map<int, std::vector<MyClass>>
和一个接受动态数量的迭代器的函数,特别是iter::zip_longest
。
I want to pass the vectors I have in the map to zip_longest
, but given that there are a dynamic number of vectors in the map, I'm not seeing a straightforward way to achieve this. 我想将地图中的矢量传递给
zip_longest
,但鉴于地图中有一些动态的矢量,我没有看到实现这一目标的简单方法。 I know that it's doable with compile time constants using templates and sizeof
, however I can't specify this at compile time. 我知道使用模板和
sizeof
编译时常量是可行的,但我不能在编译时指定它。
What I essentially want to do is 我基本上想做的是
std::vector<std::vector<MyClass>*> all_vectors;
for (const auto it : my_map) {
all_vectors.push_back(&it.second);
}
for (const auto it : iter::zip_longest(*all_vectors)) { // <-- Here using *all_vectors to unpack like in Python.
// Do stuff here
}
Let me also say that I am aware that this can be achieved through other means than zip_longest
, I'm just wondering if there is a clean way for me to use this existing function. 我还要说,我知道这可以通过
zip_longest
之外的其他方式实现,我只是想知道是否有一种干净的方式让我使用这个现有的功能。
C++ is a statically typed language. C ++是一种静态类型语言。
zip_longest
is a function that produces a zip iterator based on the number of parameters that it gets. zip_longest
是一个根据获取的参数数量生成zip迭代器的函数。 But the number of parameters a function gets is a static quantity in C++. 但是函数获得的参数数量是C ++中的静态数量。 If you need to give a function a runtime number of values, then you have to provide a runtime range or container or something of that sort.
如果需要为函数提供运行时数量的值,则必须提供运行时范围或容器或类似的东西。 And
zip_longest
isn't built for that. 并且
zip_longest
不是zip_longest
而构建的。
The static type system of C++ is static. C ++的静态类型系统是静态的。 You have a dynamic number of elements in that vector.
您在该向量中有动态数量的元素。 So you cannot have the static type system of C++ process the result, barring some fancy "generate all static types up to N then dispatch to it" that is reasonably inefficient and not all that wealdly.
所以你不能让C ++的静态类型系统处理结果,除非有一些花哨的“生成所有静态类型直到N然后调度到它”,这是相当低效的而不是所有那些。
zip_longest
is not a function. zip_longest
不是一个功能。 It is a function template. 它是一个功能模板。 A function template generates a function, often at the call site, by statically examining its arguments.
函数模板通常在调用站点通过静态检查其参数来生成函数。
Now, it would be quite possible to create an iterator that, given a dynamic vector of containers, returns a dynamic vector of elements. 现在,很有可能创建一个迭代器,给定动态容器向量,返回元素的动态向量。 The dynamic information in the source argument -- the number of elements -- corresponds to the dynamic elements in the type returned.
source参数中的动态信息 - 元素数 - 对应于返回类型中的动态元素。
So something like this is possible: 所以这样的事情是可能的:
std::vector<std::vector<MyClass>*> all_vectors;
for (const auto it : my_map) {
all_vectors.push_back(&it.second);
}
for (const auto& elems : vec_zip_longest(all_vectors)) {
// elems is a vector of boost::optional<std::ref<MyClass*>>
// Do stuff here
}
doing this for the restricted case of just for(:) loops would look something like: 为for(:)循环的受限情况执行此操作将类似于:
template<class C>
struct vec_zip_longest {
using elem_iterator = decltype( std::begin( std::declval<C&>() ) );
using elem = std::decay_t< decltype( *std::declval<elem_iterator&>() ) >;
std::vector< std::optional<elem_iterator> > current;
std::vector< elem_iterator > finish;
vec_zip_longest( std::vector<C>& vec ) {
for (auto&& c : vec )
{
current.emplace_back( std::begin(c) );
finish.emplace_back( std::end(c) );
if (current.back() == finish.back())
current.back() = {};
}
}
bool advance() {
bool retval = false;
for (std::size_t i = 0; i < current.size(); ++i) {
auto& it = current[i];
//std::cerr << "advancing\n";
if (!it)
{
//std::cerr << "already done\n";
continue;
}
++*it;
if (*it == finish[i]) {
//std::cerr << "reached end\n";
it = std::nullopt;
continue;
}
//std::cerr << "advanced\n";
retval = true;
}
return retval;
}
struct iterator {
vec_zip_longest* parent = nullptr;
friend bool operator==(iterator const& lhs, iterator const& rhs) {
return lhs.parent == rhs.parent;
}
friend bool operator!=(iterator const& lhs, iterator const& rhs) {
return lhs.parent != rhs.parent;
}
void operator++()& {
if(!parent->advance())
parent = nullptr;
}
auto operator*() {
std::vector<std::optional<std::reference_wrapper<elem>>> retval;
retval.reserve(parent->current.size());
for(auto&& oit:parent->current) {
//std::cerr << "getting element\n";
if (oit) {
//std::cerr << "something there\n";
retval.emplace_back(**oit);
} else {
//std::cerr << "nothing there\n";
retval.emplace_back();
}
}
return retval;
}
};
iterator begin() {
return {this};
}
iterator end() {
return {};
}
};
template<class C>
vec_zip_longest(std::vector<C>&)->vec_zip_longest<C>;
in c++17 , Live example . 在c ++ 17中 , 实例 。
A "more proper" iterator would take a bit more boilerplate, and you could not technically make it stronger than an input iterator. 一个“更合适”的迭代器需要更多的样板,并且你在技术上不能使它比输入迭代器更强大。 Also having support for non-member
begin
/ end
takes more work which I skipped here. 同样支持非成员
begin
/ end
需要更多的工作,我在这里跳过。
Test code: 测试代码:
std::vector<std::vector<int>> foo{ {1,2,3}, {4,5}, {6} };
vec_zip_longest zip { foo };
for (auto&& c:zip)
{
for (auto&& e : c ) {
if (e)
std::cout << *e << ",";
}
std::cout << "\n";
}
output is: 输出是:
1,4,6, 2,5, 3,
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.