(重新)将 std::algorithms 与非标准容器一起使用

2023-11-24

我有一个“列”容器类型:

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
};

Usage:

// 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)。

特别是迭代器应该提供类似的接口

struct {
  double& a;
  string& b;
  int& c;
};

Note 0:允许 C++11/C++14。

Note 1:有一张旧纸http://hci.iwr.uni-heidelberg.de/vigra/documents/DataAccessors.ps正在进行类似的尝试。但是,我无法让他们的方法与排序一起使用。使用代理类型方法很难满足像 defaultConstructible 这样的要求(为什么std::sort要求类型默认可构造而不是可交换超出了我的理解)。

Note 2:我无法执行以下操作:

struct MyType {
  double a;
  string b;
  int c;
};

std::vector<MyType> v;

然后使用std::algorithm.

动机:表现。缓存行通常为 64 字节,即 8 个双精度。在这个简单的结构中,如果您迭代双精度数,则会用字符串和 int 污染缓存行。在其他情况下,每个缓存行可能只会传输 1 个双倍数据。也就是说,您最终使用了可用内存带宽的 1/8。如果您需要迭代几个 Gb 的双精度数,这个简单的决定可以将您的应用程序性能提高 6-7 倍。不,我不能放弃这一点。

Bonus:答案应该尽可能通用。将向容器类型添加/删除字段视为向结构添加/删除成员。您不想每次添加新成员时都更改大量代码。


我认为这样的东西可能符合标准。它使用一些 C++11 功能来简化语法,但也可以更改为符合 C++03 AFAIK。

已测试并可与 clang++3.2 配合使用

Prelude:

#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();
};

迭代器类:value_type (all_copy), a reference type (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
分别定义为迭代器的差异类型、值类型和迭代器类别。此外,类型
iterator_traits<Iterator>::reference
iterator_traits<Iterator>::pointer
应定义为迭代器的引用和指针类型,即对于迭代器对象 a,其类型与 a 的类型相同*a and a->, 分别

因此,您可以引入一个结构体(all_reference)保留三个参考作为reference类型。该类型的返回值是*a, where 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;
}

Output:

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;我;9, 1;j;10,

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

(重新)将 std::algorithms 与非标准容器一起使用 的相关文章

  • 创建 DirectoryEntry 实例以供测试使用

    我正在尝试创建 DirectoryEntry 的实例 以便可以使用它来测试将传递 DirectoryEntry 的一些代码 然而 尽管进行了很多尝试 我还是找不到实例化 DE 并初始化它的 PropertyCollection 的方法 我有
  • 如何在 Unity 中从 RenderTexture 访问原始数据

    问题的简短版本 我正在尝试访问 Unity 中 RenderTexture 的内容 我一直在使用 Graphics Blit 使用自己的材质进行绘制 Graphics Blit null renderTexture material 我的材
  • 模板类的不明确多重继承

    我有一个真实的情况 可以总结为以下示例 template lt typename ListenerType gt struct Notifier void add listener ListenerType struct TimeListe
  • fgets() 和 Ctrl+D,三次才能结束?

    I don t understand why I need press Ctrl D for three times to send the EOF In addition if I press Enter then it only too
  • C# 中可空类型是什么?

    当我们必须使用nullable输入 C net 任何人都可以举例说明 可空类型 何时使用可空类型 https web archive org web http broadcast oreilly com 2010 11 understand
  • c 中的错误:声明隐藏了全局范围内的变量

    当我尝试编译以下代码时 我收到此错误消息 错误 声明隐藏了全局范围内的变量 无效迭代器 节点 根 我不明白我到底在哪里隐藏或隐藏了之前声明的全局变量 我怎样才能解决这个问题 typedef node typedef struct node
  • C# 用数组封送结构体

    假设我有一个类似于 public struct MyStruct public float a 我想用一些自定义数组大小实例化一个这样的结构 在本例中假设为 2 然后我将其封送到字节数组中 MyStruct s new MyStruct s
  • .Net Core / 控制台应用程序 / 配置 / XML

    我第一次尝试使用新的 ConfigurationBuilder 和选项模式进入 Net Core 库 这里有很多很好的例子 https docs asp net en latest fundamentals configuration ht
  • 为什么模板不能位于外部“C”块内?

    这是一个后续问题一个答案 https stackoverflow com questions 4866433 is it possible to typedef a pointer to extern c function type wit
  • 在 ASP.Net Core 2.0 中导出到 Excel

    我曾经使用下面的代码在 ASP NET MVC 中将数据导出到 Excel Response AppendHeader content disposition attachment filename ExportedHtml xls Res
  • 使用向量的 merge_sort 在少于 9 个输入的情况下效果很好

    不知何故 我使用向量实现了合并排序 问题是 它可以在少于 9 个输入的情况下正常工作 但在有 9 个或更多输入的情况下 它会执行一些我不明白的操作 如下所示 Input 5 4 3 2 1 6 5 4 3 2 1 9 8 7 6 5 4 3
  • 使用 LINQ 查找列表中特定类型的第一个元素

    使用 LINQ 和 C 在元素列表中查找特定类型的第一个项目的最短表示法是什么 var first yourCollection OfType
  • 像“1$”这样的位置参数如何与 printf() 一起使用?

    By man I find printf d width num and printf 2 1 d width num 是等价的 但在我看来 第二种风格应该与以下相同 printf d num width 然而通过测试似乎man是对的 为什
  • EPPlus Excel 更改单元格颜色

    我正在尝试将给定单元格的颜色设置为另一个单元格的颜色 该单元格已在模板中着色 但worksheet Cells row col Style Fill BackgroundColor似乎没有get财产 是否可以做到这一点 或者我是否必须在互联
  • 已过时 - OpenCV 的错误模式

    我正在使用 OpenCV 1 进行一些图像处理 并且对 cvSetErrMode 函数 它是 CxCore 的一部分 感到困惑 OpenCV 具有三种错误模式 叶 调用错误处理程序后 程序终止 Parent 程序没有终止 但错误处理程序被调
  • C++ 成员函数中的“if (!this)”有多糟糕?

    如果我遇到旧代码if this return 在应用程序中 这种风险有多严重 它是一个危险的定时炸弹 需要立即在应用程序范围内进行搜索和销毁工作 还是更像是一种可以悄悄留在原处的代码气味 我不打算writing当然 执行此操作的代码 相反
  • 期望最大化算法的数值示例[重复]

    这个问题在这里已经有答案了 由于我不确定给出的公式 有人可以提供 EM 算法的简单数字示例吗 一个非常简单的具有 4 或 5 个笛卡尔坐标的坐标就可以了 那这个呢 http en wikibooks org wiki Data Mining
  • 如何连接字符串和常量字符?

    我需要将 hello world 放入c中 我怎样才能做到这一点 string a hello const char b world const char C string a hello const char b world a b co
  • 为什么 strtok 会导致分段错误?

    为什么下面的代码给出了Seg 最后一行有问题吗 char m ReadName printf nRead String s n m Writes OK char token token strtok m 如前所述 读取字符串打印没有问题 但
  • 不同类型的指针可以互相分配吗?

    考虑到 T1 p1 T2 p2 我们可以将 p1 分配给 p2 或反之亦然吗 如果是这样 是否可以不使用强制转换来完成 或者我们必须使用强制转换 首先 让我们考虑不进行强制转换的分配 C 2018 6 5 16 1 1 列出了简单赋值的约束

随机推荐

  • 如何在 SQL Server 中使用前导通配符全文搜索?

    Note I am使用 SQL 的全文搜索功能 CONTAINS 子句和所有 是全文中的通配符 仅适用于 LIKE 子句 我现在在几个地方读到 MS SQL 不支持 前导通配符 搜索 例如使用 overflow 来匹配 stackoverf
  • MongoDB v2.4.9 按布尔字段排序

    如何根据布尔字段对查询结果进行排序 考虑以下集合 id ObjectId name John isFoo true id ObjectId name Jim isFoo false id ObjectId name Joel isFoo f
  • 兼容 Nexus 的存储库,用于获取节点和 npm 安装程序

    我正在寻找一个符合 nexus 标准的存储库 我可以在其中获取节点安装程序 一个符合 nexus 标准的替代品 http nodejs org dist 语境 在java环境中 我们的构建是由maven处理的 最近我们添加了一个 javas
  • Delphi TGIFImage 与某些 GIF 查看器的动画问题

    我发现使用 Delphi 2009 创建的动画 GIFTGIFImage有时无法正确播放someGIF 观众 问题是动画过早地重新启动 考虑以下示例 program GIFAnomaly APPTYPE CONSOLE uses Windo
  • 在 GCC 中编译:-O3 有害吗?

    我听说 gcc 不应该使用 O3 选项进行编译 真的吗 如果是这样 避免使用 O3 的原因是什么 答案是 这取决于你的代码 基本的经验法则是这样的 在 O1 时 编译器会进行不需要太长时间计算的优化 在 O2 时 编译器会进行 昂贵 的优化
  • 使用 SSH.NET 在进度栏中显示文件下载进度

    我想在我的设备上显示下载过程的进度ProgressBar 我尝试做类似的事情此代码用于上传 但我失败了 这是我失败的尝试的一个例子 private void button5 Click object sender EventArgs e T
  • 在 Firebase 中恢复数据库

    如果我在 Firebase 上删除了我的应用程序 那么一周后我想恢复它 这可能吗 我了解到有备注 确认后将永久删除 谢谢 Firebase 会在实时数据库中保留数据备份 如果您不小心从数据库中删除了数据 您可以联系 Firebase 支持并
  • 为什么在java方法重写中允许有协变返回类型,但不允许有协变参数?

    例如 我有一个 Processor 基类 其方法返回一个 Object 并以 Object 作为参数 我想扩展它并创建一个 StringProcessor 它将返回 String 并接受 String 作为参数 然而 协变类型仅允许与返回值
  • asp.net CORE 迁移生成空

    我正在尝试按照教程添加从生成的迁移 第二个 IdentityDbContext and IdentityUser 当我跑步时dotnet ef migration add
  • 在 Windows 上使用 Luarocks 安装 Torch7 并出现 mingw 构建错误

    我按照说明进行操作here并与 Mingw 从头开始 建立 Lua 和 Luarocks 一切工作正常 我能够安装rocks 包括那些需要像LuaSocket这样编译的东西 我按照说明进行操作Torch7通过 luarocks 安装 Tor
  • 焦点/按下时 TextView 颜色发生变化

    我有一些 ui 小部件 包括可点击的relativelayout 内的textview 我的问题是 尽管我已正确设置 textview 颜色属性 但当relativelayout 获得焦点时 textview 文本颜色不会改变 有没有什么简
  • Xcode 构建失败,致命错误:找不到模块“firebase_auth”@import firebase_auth;

    Doctor summary to see all details run flutter doctor v Flutter Channel stable v1 17 4 on Mac OS X 10 15 5 19F101 locale
  • 如何测试Nestjs拦截器?

    我找不到任何关于如何在 NestJS 中测试拦截器的解释 这个简单的示例拦截 POST 查询以将属性添加到正文中提供的示例模型中 Injectable export class SubscriberInterceptor implement
  • 我可以知道提交的修订号吗?

    我可以通过 svn info 等命令查看 svn 中的修订号 但在 git 中我只能看到 sha 对象名称 有什么方法可以知道已提交了多少修订 git 描述将是获取此类信息的最接近的方法 如本中所建议的其他问题 torvalds g5 gi
  • 无法使用 AVCaptureAudioDataOutputSampleDelegate 播放从语音录制的音频

    我已经用谷歌搜索和研究了好几天 但我似乎无法让它发挥作用 而且我在互联网上找不到任何解决方案 我正在尝试使用麦克风捕获我的声音 然后通过扬声器播放 这是我的代码 class ViewController UIViewController A
  • 您不应在 之外使用

    我正在尝试在示例应用程序中设置反应路由器 但出现以下错误 You should not use outside a
  • 时间:2019-05-17 标签:c#sqlwhattodispose

    我有下面的代码来从存储过程中查询记录 但担心我可能不会处理我需要的内容 或者无论如何在垃圾收集器不久之后清除该对象时正在处理 我是否需要处置 SqlDataReader 因为它位于 try catch 块内 我是否需要同时运行 cmd Di
  • Swift 中不同类型的多维数组

    当所有维度都具有相同类型时 我可以轻松地在 Swift 中编写多维数组 例如 var totalTime Int 如何使第一个维度为 String 第二个维度为 Int 我建议改用元组数组 你想要什么could可以使用 Any 类型的数组来
  • 如何寻找有用的红宝石

    有哪些寻找有用红宝石的好网站 敏捷网络开发列出插件 虽然不是 ruby gems 我不知道为什么 并允许人们对它们进行评分 红宝石工具箱按类别列出宝石并比较它们的受欢迎程度 Rubygems有一个搜索框 堆栈溢出对最有用的 Rails 插件
  • (重新)将 std::algorithms 与非标准容器一起使用

    我有一个 列 容器类型 struct MyColumnType Data Each row represents a member of an object vector