- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我有一个“列”容器类型:
struct MyColumnType {
// Data: Each row represents a member of an object.
vector<double> a; // All vectors are guaranteed to have always
vector<string> b; // the same length.
vector<int> c;
void copy(int from_pos, int to_pos); // The column type provides an interface
void swap(int pos_a, int pos_b); // for copying, swapping, ...
void push_back(); // And for resizing the container.
void pop_back();
void insert(int pos);
void remove(int pos);
// The interface can be extended/modified if required
};
用法:
// If table is a constructed container with elements stored
// To acces the members of the object stored at the 4th position:
table.a[4] = 4.0;
table.b[4] = "4th";
table.c[4] = 4;
问题如何为这种容器创建一个符合标准的随机访问迭代器(可能还有一个必需的代理引用类型)?
我希望能够将 std::algorithms
用于我的类型的随机访问迭代器,例如sort
(注意:用于排序的比较将由用户定义的仿函数提供,例如 lambda)。
特别是迭代器应该提供类似于
的接口(interface)struct {
double& a;
string& b;
int& c;
};
注意 0: C++11/C++14 是允许的。
注1:有一篇旧论文http://hci.iwr.uni-heidelberg.de/vigra/documents/DataAccessors.ps进行类似尝试的地方。但是,我无法让他们的方法与排序一起工作。使用代理类型方法很难满足像 defaultConstructible 这样的要求(为什么 std::sort
要求类型是默认可构造的而不是可交换的,这超出了我的理解)。
注意 2:我不能执行以下操作:
struct MyType {
double a;
string b;
int c;
};
std::vector<MyType> v;
然后使用std::algorithm
。
动机:表现。缓存行通常为 64 字节,即 8 个双字节。在这个简单的结构中,如果你遍历 double ,你就会用一个字符串和一个 int 污染缓存行。在其他情况下,每个缓存行可能只会传输 1 个双倍数据。也就是说,您最终使用了可用内存带宽的 1/8。如果您需要迭代几 Gb 的 double ,这个简单的决定可以将您的应用程序性能提高 6-7 倍。不,我不能放弃。
奖励:答案应尽可能通用。将向容器类型添加/删除字段视为向结构添加/删除成员。您不希望每次添加新成员时都更改大量代码。
最佳答案
我认为像这样的东西可以符合标准。它使用一些 C++11 功能来简化语法,但也可以更改为符合 C++03 AFAIK。
经过测试并适用于 clang++3.2
前奏:
#include <vector>
#include <string>
#include <utility> // for std::swap
#include <iterator>
using std::vector;
using std::string;
// didn't want to insert all those types as nested classes of MyColumnType
namespace MyColumnType_iterator
{
struct all_copy;
struct all_reference;
struct all_iterator;
}
// just provided `begin` and `end` member functions
struct MyColumnType {
// Data: Each row represents a member of an object.
vector<double> a; // All vectors are guaranteed to have always
vector<string> b; // the same length.
vector<int> c;
void copy(int from_pos, int to_pos); // The column type provides an itface
void swap(int pos_a, int pos_b); // for copying, swapping, ...
void push_back(); // And for resizing the container.
void pop_back();
void insert(int pos);
void remove(int pos);
// The interface can be extended/modified if required
using iterator = MyColumnType_iterator::all_iterator;
iterator begin();
iterator end();
};
迭代器类:a value_type
( all_copy
), 一个 reference
类型 ( all_reference
) 和迭代器类型 ( all_iterator
)。迭代是通过保留和更新三个迭代器(每个迭代器一个 vector
)来完成的。不过,我不知道这是否是性能最高的选项。
工作原理:std::iterator_traits
为迭代器定义了几个关联类型:[迭代器.traits]/1
iterator_traits<Iterator>::difference_type
iterator_traits<Iterator>::value_type
iterator_traits<Iterator>::iterator_category
be defined as the iterator’s difference type, value type and iterator category, respectively. In addition, the types
iterator_traits<Iterator>::reference
iterator_traits<Iterator>::pointer
shall be defined as the iterator’s reference and pointer types, that is, for an iterator object a, the same type as the type of*a
anda->
, respectively
因此,您可以引入一个结构体 ( all_reference
) 将三个引用保留为 reference
类型。这个类型就是*a
的返回值, 其中a
是迭代器类型(可能是 const
限定的)。需要有一个不同的 value_type
因为一些标准库算法,例如 sort
可能想要创建一个局部变量来临时存储 *a
的值(通过复制或移动到局部变量)。在这种情况下,all_copy
提供此功能。
您不需要在您自己的循环中使用它 ( all_copy
),因为它可能会影响性能。
namespace MyColumnType_iterator
{
struct all_copy;
struct all_reference
{
double& a;
string& b;
int& c;
all_reference() = delete;
// not required for std::sort, but stream output is simpler to write
// with this
all_reference(all_reference const&) = default;
all_reference(double& pa, string& pb, int& pc)
: a{pa}
, b{pb}
, c{pc}
{}
// MoveConstructible required for std::sort
all_reference(all_reference&& other) = default;
// MoveAssignable required for std::sort
all_reference& operator= (all_reference&& other)
{
a = std::move(other.a);
b = std::move(other.b);
c = std::move(other.c);
return *this;
}
// swappable required for std::sort
friend void swap(all_reference p0, all_reference p1)
{
std::swap(p0.a, p1.a);
std::swap(p0.b, p1.b);
std::swap(p0.c, p1.c);
}
all_reference& operator= (all_copy const& p) = default;
all_reference& operator= (all_copy&& p) = default;
// strict total ordering required for std::sort
friend bool operator< (all_reference const& lhs,
all_reference const& rhs);
friend bool operator< (all_reference const& lhs, all_copy const& rhs);
friend bool operator< (all_copy const& lhs, all_reference const& rhs);
};
struct all_copy
{
double a;
string b;
int c;
all_copy(all_reference const& p)
: a{p.a}
, b{p.b}
, c{p.c}
{}
all_copy(all_reference&& p)
: a{ std::move(p.a) }
, b{ std::move(p.b) }
, c{ std::move(p.c) }
{}
};
std::sort
需要比较函数.出于某种原因,我们必须提供所有这三个。
bool operator< (all_reference const& lhs, all_reference const& rhs)
{
return lhs.c < rhs.c;
}
bool operator< (all_reference const& lhs, all_copy const& rhs)
{
return lhs.c < rhs.c;
}
bool operator< (all_copy const& lhs, all_reference const& rhs)
{
return lhs.c < rhs.c;
}
现在,迭代器类:
struct all_iterator
: public std::iterator < std::random_access_iterator_tag, all_copy >
{
//+ specific to implementation
private:
using ItA = std::vector<double>::iterator;
using ItB = std::vector<std::string>::iterator;
using ItC = std::vector<int>::iterator;
ItA iA;
ItB iB;
ItC iC;
public:
all_iterator(ItA a, ItB b, ItC c)
: iA(a)
, iB(b)
, iC(c)
{}
//- specific to implementation
//+ for iterator_traits
using reference = all_reference;
using pointer = all_reference;
//- for iterator_traits
//+ iterator requirement [iterator.iterators]/1
all_iterator(all_iterator const&) = default; // CopyConstructible
all_iterator& operator=(all_iterator const&) = default; // CopyAssignable
~all_iterator() = default; // Destructible
void swap(all_iterator& other) // lvalues are swappable
{
std::swap(iA, other.iA);
std::swap(iB, other.iB);
std::swap(iC, other.iC);
}
//- iterator requirements [iterator.iterators]/1
//+ iterator requirement [iterator.iterators]/2
all_reference operator*()
{
return {*iA, *iB, *iC};
}
all_iterator& operator++()
{
++iA;
++iB;
++iC;
return *this;
}
//- iterator requirement [iterator.iterators]/2
//+ input iterator requirements [input.iterators]/1
bool operator==(all_iterator const& other) const // EqualityComparable
{
return iA == other.iA; // should be sufficient (?)
}
//- input iterator requirements [input.iterators]/1
//+ input iterator requirements [input.iterators]/2
bool operator!=(all_iterator const& other) const // "UnEqualityComparable"
{
return iA != other.iA; // should be sufficient (?)
}
all_reference const operator*() const // *a
{
return {*iA, *iB, *iC};
}
all_reference operator->() // a->m
{
return {*iA, *iB, *iC};
}
all_reference const operator->() const // a->m
{
return {*iA, *iB, *iC};
}
// ++r already satisfied
all_iterator operator++(int) // *++r
{
all_iterator temp(*this);
++(*this);
return temp;
}
//- input iterator requirements [input.iterators]/2
//+ output iterator requirements [output.iterators]/1
// *r = o already satisfied
// ++r already satisfied
// r++ already satisfied
// *r++ = o already satisfied
//- output iterator requirements [output.iterators]/1
//+ forward iterator requirements [forward.iterators]/1
all_iterator() = default; // DefaultConstructible
// r++ already satisfied
// *r++ already satisfied
// multi-pass must be guaranteed
//- forward iterator requirements [forward.iterators]/1
//+ bidirectional iterator requirements [bidirectional.iterators]/1
all_iterator& operator--() // --r
{
--iA;
--iB;
--iC;
return *this;
}
all_iterator operator--(int) // r--
{
all_iterator temp(*this);
--(*this);
return temp;
}
// *r-- already satisfied
//- bidirectional iterator requirements [bidirectional.iterators]/1
//+ random access iterator requirements [random.access.iterators]/1
all_iterator& operator+=(difference_type p) // r += n
{
iA += p;
iB += p;
iC += p;
return *this;
}
all_iterator operator+(difference_type p) const // a + n
{
all_iterator temp(*this);
temp += p;
return temp;
}
// doesn't have to be a friend function, but this way,
// we can define it here
friend all_iterator operator+(difference_type p,
all_iterator temp) // n + a
{
temp += p;
return temp;
}
all_iterator& operator-=(difference_type p) // r -= n
{
iA -= p;
iB -= p;
iC -= p;
return *this;
}
all_iterator operator-(difference_type p) const // a - n
{
all_iterator temp(*this);
temp -= p;
return temp;
}
difference_type operator-(all_iterator const& p) // b - a
{
return iA - p.iA; // should be sufficient (?)
}
all_reference operator[](difference_type p) // a[n]
{
return *(*this + p);
}
all_reference const operator[](difference_type p) const // a[n]
{
return *(*this + p);
}
bool operator<(all_iterator const& p) const // a < b
{
return iA < p.iA; // should be sufficient (?)
}
bool operator>(all_iterator const& p) const // a > b
{
return iA > p.iA; // should be sufficient (?)
}
bool operator>=(all_iterator const& p) const // a >= b
{
return iA >= p.iA; // should be sufficient (?)
}
bool operator<=(all_iterator const& p) const // a >= b
{
return iA <= p.iA; // should be sufficient (?)
}
//- random access iterator requirements [random.access.iterators]/1
};
}//- namespace MyColumnType_iterator
MyColumnType::iterator MyColumnType::begin()
{
return { a.begin(), b.begin(), c.begin() };
}
MyColumnType::iterator MyColumnType::end()
{
return { a.end(), b.end(), c.end() };
}
使用示例:
#include <iostream>
#include <cstddef>
#include <algorithm>
namespace MyColumnType_iterator
{
template < typename char_type, typename char_traits >
std::basic_ostream < char_type, char_traits >&
operator<< (std::basic_ostream < char_type, char_traits >& o,
std::iterator_traits<MyColumnType::iterator>::reference p)
{
return o << p.a << ";" << p.b << ";" << p.c;
}
}
int main()
{
using std::cout;
MyColumnType mct =
{
{1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1}
, {"j", "i", "h", "g", "f", "e", "d", "c", "b", "a"}
, {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}
};
using ref = std::iterator_traits<MyColumnType::iterator>::reference;
std::copy(mct.begin(), mct.end(), std::ostream_iterator<ref>(cout, ", "));
std::cout << std::endl;
std::sort(mct.begin(), mct.end());
std::copy(mct.begin(), mct.end(), std::ostream_iterator<ref>(cout, ", "));
std::cout << std::endl;
}
输出:
1;j;10, 0.9;i;9, 0.8;h;8, 0.7;g;7, 0.6;f;6, 0.5;e;5, 0.4;d;4, 0.3;c;3, 0.2;b;2, 0.1;a;1,
0.1;a;1, 0.2;b;2, 0.3;c;3, 0.4;d;4, 0.5;e;5, 0.6;f;6, 0.7;g;7, 0.8;h;8, 0.9;i;9, 1;j;10,
关于c++ - (Re)Using std::algorithms with non-standard containers,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16874183/
我正在使用 gmock 并模拟了一个函数 boost::beast::http::response_parser作为输出参数。功能签名看起来像: error_code readFromSocket(b
我的任务是打印由“非元音、元音、非元音”组成的单词列表,即 bab、bac、bad、bad ... 到 zuz。 我已经设法创建了一个代码,它执行前两个字母,但在最后一个循环中丢失并只打印'}' -
我正在尝试使用 label2rgb 生成 RGB 标签切片并使用它来更新 RGB 体积,如下所示: labelRGB_slice=label2rgb(handles.label(:,:,han
我有一个很奇怪的问题。我在 dll 中定义了一个接口(interface),如下所示: public interface IKreator2 { string Name { get; set;
在我的 openshift Drupal 托管中,网络都在 SSL 下 http://domain.com -> https://www.domain.com 确定 http://www.domain
我收到警告“退出构造函数时不可为空的事件‘SomeEvent’必须包含非空值。考虑将事件声明为可空。” 这是我的代码的一个非常简化的版本,它复制了完全相同的问题。我在这里错过了什么?这与 .Net 6
在一次大学考试中,我被要求测试一些 apache 簿记员类/方法,在这样做的过程中,我想在我的参数化测试中使用 mockito。没有 mockito 的测试工作正常但是当我尝试模拟接口(interfa
假设 A 列在 7 行中有以下值: 2 [空白的] 0 -0.3 0 [空白的] 0 如何获取范围(7 行)中非空/空白且不为零的最后一个值?因此,在这种情况下,正确答案是 -0.3。 最佳答案 =I
考虑以下受 this talk 启发的代码: template struct even_common_type_helper_impl; template struct even_common_typ
考虑这段代码, struct A {}; struct B { B(const A&) {} }; void f(B) { cout << "f()"<
考虑下面的类(class)。如果我对它运行 Findbugs,它会在第 5 行但不在第 7 行给我一个错误(“可序列化类中的非 transient 非可序列化实例字段”)。 1 public clas
我正在编写一个 python 脚本来计算 数据包丢失 通过使用 ping IP 地址linux 中的 subprocess 模块。 CSV 文件中保存了多个 IP 地址。当只给出可 ping 目的地时
我只是做文本更改,在文本之前它工作正常。请任何人都可以帮助我。 提前致谢 最佳答案 我已经解决了: ionic cordova 插件rmcordova-plugin-ionic-webview ion
我如何定义 在 persistence.xml 中? 我的项目在 Tomcat 6 和 Tomcat 7 中运行良好。 现在我正在使用 Struts 2 Spring 3.0.5 JPA 2 Jbos
我有一个 maven 仓库中不存在的第三方 jar,我们称它为“a.jar”,它也依赖于至少 20 多个第三方 jar,其中大部分不在 maven 中或者,我们称它们为“b.jar、c.jar、d.j
我已经浏览了各种线程很多小时(不夸张),但一直无法找到一种解决方案组合,使我能够将非 www 和 http 转发到 www 和 https,同时仍然能够查看 php 文件没有扩展名。如下是我的ngin
Scott Meyer 关于非成员函数增加封装并允许更优雅的设计(设计方面)的论点对我来说似乎非常有效。看这里:Article 但是我对此有疑问。 (似乎还有其他人,尤其是库开发人员,他们通常完全忽略
在对类设计的一些事实感到困惑时,特别是函数是否应该是成员,我查看了 Effective c++ 并找到了第 23 条,即 Prefer non-member non-friend functions
我正在尝试使用 firebase 云功能将通知发送到一个点半径的圆内的设备。我能够获取圈内设备的 ID,但无法获取 token ,使用 console.log(token) 打印时 token 为空。
我在我的项目中使用 React-ckeditor 5 包。我得到一个反序列化的 html 数据,我正在使用 React-html-parser 包将它解析成 html 模板,并将这个解析的数据传递给
我是一名优秀的程序员,十分优秀!