我在设计一个简单的 zip 函数时遇到了一个问题,可以这样调用:
for (auto [x, y] : zip(std::vector{1,2,3}, std:vector{-1, -2, -3}) {
// ...
}
所以 zip将返回类型为 zip_range 的对象,本身暴露begin和 end返回 zip_iterator 的函数.
现在,一个 zip_iterator ,正如我实现的那样,使用 std::tuple<Iterators> - 其中 Iterators 是压缩容器迭代器的类型 - 以跟踪其在压缩容器中的位置。当我取消引用 zip_iterator 时,我获得了对压缩容器元素的引用元组。问题是它不适合结构化绑定(bind)语法:
std::vector a{1,2,3}, b{-1, -2, -3};
for (auto [x, y] : zip(a, b)) { // syntax suggests by value
std::cout << ++x << ", " << --y << '\n'; // but this affects a's and b's content
}
for (auto& [x, y] : zip(a, b)) { // syntax suggests by reference
// fails to compile: binding lvalue ref to temporary
}
所以我的问题是:你能找到一种方法来协调这个引用元组的实际类型(临时值)与其语义(左值,允许修改它所引用的内容)吗?
我希望我的问题不会太宽泛。这是一个工作示例,用 clang++ prog.cc -Wall -Wextra -std=gnu++2a 编译(由于 gcc 处理演绎指南的方式存在错误,它不适用于 gcc):
#include <tuple>
#include <iterator>
#include <iostream>
#include <vector>
#include <list>
#include <functional>
template <typename Fn, typename Argument, std::size_t... Ns>
auto tuple_map_impl(Fn&& fn, Argument&& argument, std::index_sequence<Ns...>) {
if constexpr (sizeof...(Ns) == 0) return std::tuple<>(); // empty tuple
else if constexpr (std::is_same_v<decltype(fn(std::get<0>(argument))), void>) {
[[maybe_unused]]
auto _ = {(fn(std::get<Ns>(argument)), 0)...}; // no return value expected
return;
}
// then dispatch lvalue, rvalue ref, temporary
else if constexpr (std::is_lvalue_reference_v<decltype(fn(std::get<0>(argument)))>) {
return std::tie(fn(std::get<Ns>(argument))...);
}
else if constexpr (std::is_rvalue_reference_v<decltype(fn(std::get<0>(argument)))>) {
return std::forward_as_tuple(fn(std::get<Ns>(argument))...);
}
else {
return std::tuple(fn(std::get<Ns>(argument))...);
}
}
template <typename T>
constexpr bool is_tuple_impl_v = false;
template <typename... Ts>
constexpr bool is_tuple_impl_v<std::tuple<Ts...>> = true;
template <typename T>
constexpr bool is_tuple_v = is_tuple_impl_v<std::decay_t<T>>;
template <typename Fn, typename Tuple>
auto tuple_map(Fn&& fn, Tuple&& tuple) {
static_assert(is_tuple_v<Tuple>, "tuple_map implemented only for tuples");
return tuple_map_impl(std::forward<Fn>(fn), std::forward<Tuple>(tuple),
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>());
}
template <typename... Iterators>
class zip_iterator {
public:
using value_type = std::tuple<typename std::decay_t<Iterators>::value_type...>;
using difference_type = std::size_t;
using pointer = value_type*;
using reference = value_type&;
using iterator_category = std::forward_iterator_tag;
public:
zip_iterator(Iterators... iterators) : iters(iterators...) {}
zip_iterator(const std::tuple<Iterators...>& iter_tuple) : iters(iter_tuple) {}
zip_iterator(const zip_iterator&) = default;
zip_iterator(zip_iterator&&) = default;
zip_iterator& operator=(const zip_iterator&) = default;
zip_iterator& operator=(zip_iterator&&) = default;
bool operator != (const zip_iterator& other) const { return iters != other.iters; }
zip_iterator& operator++() {
tuple_map([](auto& iter) { ++iter; }, iters);
return *this;
}
zip_iterator operator++(int) {
auto tmp = *this;
++(*this);
return tmp;
}
auto operator*() {
return tuple_map([](auto i) -> decltype(auto) { return *i; }, iters);
}
auto operator*() const {
return tuple_map([](auto i) -> decltype(auto) { return *i; }, iters);
}
private:
std::tuple<Iterators...> iters;
};
template <typename... Containers>
struct zip {
using iterator = zip_iterator<decltype(std::remove_reference_t<Containers>().begin())...>;
template <typename... Container_types>
zip(Container_types&&... containers) : containers_(containers...) {}
auto begin() { return iterator(tuple_map([](auto&& i) { return std::begin(i); }, containers_)); }
auto end() { return iterator(tuple_map([](auto&& i) { return std::end(i); }, containers_)); }
std::tuple<Containers...> containers_;
};
template <typename... Container_types>
zip(Container_types&&... containers) -> zip<std::conditional_t<std::is_lvalue_reference_v<Container_types>,
Container_types,
std::remove_reference_t<Container_types>>...>;
int main() {
std::vector a{1,2,3}, b{-1, -2, -3};
for (auto [x, y] : zip(a, b)) { // syntax suggests by value
std::cout << x++ << ", " << y-- << '\n'; // but this affects a's and b's content
}
for (auto [x, y] : zip(a, b)) {
std::cout << x << ", " << y << '\n'; // new content
}
//for (auto& [x, y] : zip(a, b)) { // syntax suggests by reference
// fails to compile: binding lvalue ref to temporary
//}
}
最佳答案
从技术上讲,这不是结构化绑定(bind)问题,而是引用语义类型问题。 auto x = y看起来它确实是在复制然后作用于一个独立的类型,对于 tuple<T&...> 这样的类型来说显然不是这种情况。 (以及 reference_wrapper<T> 和 string_view 和 span<T> 等)。
然而,作为 T.C.在评论中建议,您可以做一些可怕的事情来完成这项工作。请注意,实际上不要这样做。我认为你的实现是正确的。但只是为了完整性。以及普遍的兴趣。
首先,结构化绑定(bind)的措辞表明了 get() 的不同之处。根据 underlying object 的值类别调用.如果它是左值引用(即 auto& 或 auto const& ),则 get()在左值上调用。否则,它在 xvalue 上调用。我们需要通过以下方式利用这一点:
for (auto [x, y] : zip(a, b)) { ... }
做一件事,然后
for (auto& [x, y] : zip(a, b)) { ... }
做点别的。首先需要实际编译其他东西。为此,您的 zip_iterator::operator*需要返回一个左值。为此,它实际上需要在其中存储一个 tuple<T&...>。 .最简单的方法(在我看来)是存储一个 optional<tuple<T&...>>并且有operator*做一个 emplace()在上面并返回它的 value() .即:
template <typename... Iterators>
class zip_iterator {
// ...
auto& operator*() {
value.emplace(tuple_map([](auto i) -> decltype(auto) { return *i; }, iters));
return *value;
}
// no more operator*() const. You didn't need it anyway?
private:
std::tuple<Iterators...> iters;
using deref_types = std::tuple<decltype(*std::declval<Iterators>())...>;
std::optional<deref_types> value;
};
但这仍然给我们带来了想要不同 get() 的问题秒。为了解决这个问题,我们需要自己的 tuple类型 - 提供自己的 get() s,这样当调用一个左值时它产生左值,但是当调用一个 xvalue 时它产生纯右值。
我认为是这样的:
template <typename... Ts>
struct zip_tuple : std::tuple<Ts...> {
using base = std::tuple<Ts...>;
using base::base;
template <typename... Us,
std::enable_if_t<(std::is_constructible_v<Ts, Us&&> && ...), int> = 0>
zip_tuple(std::tuple<Us...>&& rhs)
: base(std::move(rhs))
{ }
template <size_t I>
auto& get() & {
return std::get<I>(*this);
};
template <size_t I>
auto& get() const& {
return std::get<I>(*this);
};
template <size_t I>
auto get() && {
return std::get<I>(*this);
};
template <size_t I>
auto get() const&& {
return std::get<I>(*this);
};
};
namespace std {
template <typename... Ts>
struct tuple_size<zip_tuple<Ts...>>
: tuple_size<tuple<Ts...>>
{ };
template <size_t I, typename... Ts>
struct tuple_element<I, zip_tuple<Ts...>>
: tuple_element<I, tuple<remove_reference_t<Ts>...>>
{ };
}
在非左值引用的情况下,这意味着我们绑定(bind)了一堆 rvalue references到临时工,这很好——他们的生命周期得到延长。
现在只改变 deref_types别名为 zip_tuple而不是 std::tuple并且你有你想要的行为。
两个不相关的笔记。
1) 您的演绎指南可以简化为:
template <typename... Container_types>
zip(Container_types&&... containers) -> zip<Container_types...>;
如果Container_types不是左值引用类型,那么它就不是引用类型,remove_reference_t<Container_types> 是 Container_types .
2) gcc 有 a bug关于你试图构建 zip<> 的方式.因此,要使其与两者一起编译,请选择:
template <typename... Containers>
struct zip {
zip(Containers... containers) : containers_(std::forward<Containers>(containers)...) { }
};
无论如何,您的预期用途是通过演绎指南,所以这不应该让您付出任何代价来支持它在多个编译器上工作。
关于c++ - 结构化绑定(bind)和引用元组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49628401/
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput
我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的rubyyaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最
如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:
我正在尝试在Rails上安装ruby,到目前为止一切都已安装,但是当我尝试使用rakedb:create创建数据库时,我收到一个奇怪的错误:dyld:lazysymbolbindingfailed:Symbolnotfound:_mysql_get_client_infoReferencedfrom:/Library/Ruby/Gems/1.8/gems/mysql2-0.3.11/lib/mysql2/mysql2.bundleExpectedin:flatnamespacedyld:Symbolnotfound:_mysql_get_client_infoReferencedf
您将如何构建一个简单的Sinatra应用程序?我正在制作,我希望该应用具有以下功能:“应用程序”更像是一个包含所有信息的管理仪表板。然后另一个应用程序将通过REST访问信息。我还没有创建仪表板,只是从数据库中获取东西session和身份验证(尚未实现)您可以上传图片,其他应用可以显示这些图片我已经使用RSpec创建了一个测试文件通过Prawn生成报告目前的设置是这样的:app.rbtest_app.rb因为我实际上只有应用程序和测试文件。到目前为止,我已经将Datamapper用于ORM,将SQLite用于数据库。这是我的第一个Ruby/Sinatra项目,所以欢迎任何和所有建议-我应
我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我
我正在尝试将一个资源属性的默认值设置为另一个属性的值。我正在为我正在构建的tomcat说明书定义一个资源,其中包含以下定义。我想要可以独立设置的“名称”和“服务名称”属性。当未设置服务名称时,我希望它默认为为“名称”提供的任何内容。以下不符合我的预期:attribute:name,:kind_of=>String,:required=>true,:name_attribute=>trueattribute:service_name,:kind_of=>String,:default=>:name注意第二行末尾的“:default=>:name”。当我在Recipe的新block中引用我