带模板的 N 维嵌套金属循环

2024-01-26

我正在尝试使用模板元编程进行 N 维嵌套金属循环。 嵌套部分很简单,但是将所有任意数量的迭代索引作为模板参数传递到最内层循环似乎有问题。

一个简单的未嵌套的金属环看起来像:

template <size_t I, size_t N>
struct meta_for
{
    template <typename Lambda>
    inline meta_for(Lambda &&iteration)
    {
        iteration(I);
        meta_for<I+1, N> next(static_cast<Lambda&&>(iteration));
    }
};

template <size_t N>
struct meta_for<N, N>
{
    template <typename Lambda>
    inline meta_for(Lambda &&iteration)
    {
        return;
    }
};

#include <iostream>

int main()
{
    meta_for<0, 10>([&](size_t i) // perform 10 iterations
    {
        std::cout << i << '\n';
    });

    return 0;
}

现在,我想制作一个metaloop,它接受一个表示维度(嵌套级别)的 N 参数,使用如下:

#include <iostream>

int main()
{
    // perform 3 dimensionally nested iterations
    // each index goes from 0 to 10
    // so 10x10x10 iterations performed
    meta_for<3, 0, 10>([&](size_t i, size_t j, size_t k)
    {
        std::cout << i << ' ' << j << ' ' << k << '\n';
    });

    return 0;
}

由于这个问题似乎仍然很受欢迎,我认为最好展示一下在 C++17 中这样做是多么容易。一、完整代码

Demo https://wandbox.org/permlink/YVU8ehBqck6a0K8m

template<size_t Dimensions, class Callable>
constexpr void meta_for_loop(size_t begin, size_t end, Callable&& c)
{
    static_assert(Dimensions > 0);
    for(size_t i = begin; i != end; ++i)
    {
        if constexpr(Dimensions == 1)
        {
            c(i);
        }
        else
        {
            auto bind_an_argument = [i, &c](auto... args)
            {
                c(i, args...);
            };
            meta_for_loop<Dimensions-1>(begin, end, bind_an_argument);
        }
    }
}

解释:

  1. 如果维度为 1,我们只需在循环中使用下一个索引调用提供的 lambda
  2. 否则,我们从提供的可调用对象创建一个新的可调用对象,只不过我们将循环索引绑定到可调用参数之一。然后我们在元 for 循环上递归,减少 1 维。

如果您非常熟悉函数式编程,那么这会更容易理解,因为它是currying https://en.wikipedia.org/wiki/Currying.

更具体地说它是如何工作的:

你想要一个二进制计数器

0 0
0 1
1 0
1 1

因此,您创建一个可以打印两个整数的可调用函数,如下所示:

auto callable = [](size_t i, size_t j)
{
   std::cout << i << " " << j << std::endl;
};

由于我们有两列,因此有两个维度,因此 D = 2。

我们将上面定义的元 for 循环称为如下:

meta_for_loop<2>(0, 2, callable);

The end论证meta_for_loop是 2 而不是 1,因为我们正在建模一个半闭区间 [start, end),这在编程中很常见,因为人们经常希望第一个索引包含在循环中,然后他们想要迭代 (end - start)次。

让我们逐步了解该算法:

  1. 维度 == 2,所以我们的静态断言不会失败
  2. 我们开始迭代,i = 0
  3. Dimensions == 2, so we enter the "else" branch of our constexpr if https://en.cppreference.com/w/cpp/language/if statement
    • 我们创建一个新的可调用对象来捕获传入的可调用对象并为其命名bind_an_argument以反映我们正在绑定所提供的可调用对象的一个​​参数c.

So, bind_an_argument实际上看起来像这样:

void bind_an_argument(size_t j)
{
    c(i, j);
}

注意i保持不变,但是j是可变的。这在我们的元 for 循环中很有用,因为我们想要模拟这样一个事实:外部循环保持相同的索引,而内部循环在其整个范围内迭代。 例如

for(int i = 0; i < N; ++i)
{
    for (int j = 0; j < M; ++j)
    {
       /*...*/
    }
}

when i == 0我们迭代所有值j from 0 to M,然后我们重复i == 1, i == 2, etc.

  1. We call meta_for_loop再次,除了Dimensions is now 1代替2,以及我们的Callable is now bind_an_argument代替c
  2. Dimensions == 1 so our static_assert passes
  3. 我们开始循环for(size_t i = 0; i < 2; ++i)
  4. Dimensions == 1所以我们输入if我们的分行constexpr if
  5. We call bind_an_argument with i = 1, which calls our callable from above with arguments (0, 0), the first of which was bound from the previous call to meta_for_loop. This produces output

    0 0

  6. We call bind_an_argument with i == 1, which calls our callable from above with arguments (0, 1), the first argument of which was bound during our previous call to meta_for_loop. This produces output

    0 1

  7. 我们完成迭代,因此堆栈展开到父调用函数
  8. 我们再次致电meta_for_loop with Dimensions == 2 and Callable == callable。我们完成第一次循环迭代,然后递增i to 1
  9. Since Dimensions == 2,我们输入else再次分支
  10. Repeat steps 4 through 10, except that the first argument to callable is bound to 1 instead of 0. This produces output

    1 0
    1 1

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

带模板的 N 维嵌套金属循环 的相关文章

随机推荐

  • 如何将活动指示器放在表格视图中间

    我已经设置了活动指示器 spinner center CGPointMake UIScreen mainScreen bounds size width 2 UIScreen mainScreen bounds size height 2
  • 较大的纹理图集会因内存压力而导致终止

    我正在 iOS Swift 中设计一款游戏 我有一个大约 100 个 1920x1080p PNG 的大型纹理图集 当我调用这些函数时 它会由于内存压力而导致应用程序崩溃 当我禁用该功能时 我的应用程序运行得很好 我可以通过编辑代码来防止这
  • MongoMapper(或任何其他 Mongodb 适配器)是否有类似“accepts_nested_attributes_for”的方法?

    我正在考虑在一个新项目中使用 mongodb 但在深入研究之前 我想知道它是否支持一些关键功能 我知道您不需要迁移 并且可以添加嵌入对象 但这是否意味着它的行为也就像 accepts nested attributes for 方法始终存在
  • 给定一组 0-9 的整数,在用完某个整数之前我能写的最后一个数字是多少?

    正如标题所说 给定一组整数 0 9 在用完某个整数之前我能写的最后一个数字是多少 因此 如果给我一个库存 比如从 0 到 9 的每个数字都有 10 个 那么在用完某个数字之前我可以写的最后一个数字是多少 例如 如果库存为 2 我可以写数字
  • 登录会员.NET

    您好 我想知道强制用户在到达 net 网站时登录的最佳方法是什么 我已经设置了会员功能 我想知道什么是最好的方法来确保无论用户到达哪个地址 他们都必须首先经过身份验证 然后才能进入他们请求的页面 任何资源都会很棒 谢谢 Alter Web
  • 尝试设置 .SSH 密钥以通过 Cygwin 连接到 Github

    我刚刚在 Windows 7 上安装了 Cygwin 来开发 RoR 应用程序 问题是我试图通过 Cygwin 将 Git 与我的 Github 帐户连接起来 但由于某种原因 当我运行命令时ssh keygen t rsa C email
  • Parse API - Facebook 登录无法在 iPhone 设备上运行

    当我点击 使用 Facebook 登录 按钮时 在 IOS 模拟器上它工作正常 获取 PFIzer 对象 然后 如果我在 iPhone 设备上运行相同的代码 则获取 PFUser 对象为 null 没有错误 Code func loginW
  • 表模块与域模型

    我问的是选择存储用户配置文件的方法 https stackoverflow com questions 429878 choosing a method to store user profiles前几天收到了大卫 托马斯 加西亚的有趣回应
  • 将多次出现的特殊字符替换为单个特殊字符

    我想删除多次出现的特殊字符 例如 从我的 java 字符串中通过一个下划线 我试过 replaceAll s 1 它似乎用下划线替换连续的相同类型的特殊字符 但否则不起作用 例如 Hello World becomes Hello Worl
  • 如何使用货币轨显示带有符号的价格

    鉴于这个简单Money对象查询 Money new 1000 USD to s gt 10 00 如何用其符号显示该值 我知道我可以打电话money object symbol但有些货币将符号放在值之前 而另一些货币则将符号放在值之后 我很
  • 如何获取ViewFlipper当前的子位置

    我已将一堆图像添加到 ViewFlipper 现在我正在 Flipper 中执行 onClick 事件 为此 我想知道当前的子位置 以便我可以在数组中执行一些操作 无论如何 有没有办法找出当前孩子的位置 使用它来获取当前的子位置 flipp
  • 离线网络应用程序加载时内容会消失几分之一秒

    我在 iOS 4 3 到 5 0 上观察到了这一点 当您创建一个简单的离线 Web 应用程序时 即一个 HTML 文件 一些资源 例如 CSS 和 JS 全部存在于缓存清单中 它可以离线工作 我在飞行模式下进行了测试 BUT 当您将这样的应
  • SDL_GetKeyboardState 不起作用

    我正在尝试使用 SDL 2 为游戏制作一个控制器 不想询问 gamedev 因为这不是直接的游戏问题 我使用 SDL GetKeyboardEvent 来查看导航箭头是否被按下 但它显然不起作用 如果按下其中一个键 它应该打印值 1 或 1
  • 使用 Cloud SDK CLI One-Liner 获取默认 GCP 项目 ID

    我正在寻找一个gcloud一行代码获取默认项目 ID GCP PROJECT ID The list命令给了我 gcloud config list core project gt core project GCP PROJECT ID Y
  • Logcat 显示空 SMPTE 2094-40 数据

    当我将设备连接到 android studio logcat 时 它不断显示此消息 Empty SMPTE 2094 40 data 有谁知道这是从哪里来的或如何阻止它 在Android Studio中 尝试使用这个过滤器 package
  • @storybook/Angular 无法在故事索引上加载 scss 文件

    我一直在尝试在我的角度项目中使用故事书 并且我使用本指南https storybook js org basics guide angular https storybook js org basics guide angular 我将推荐
  • 从 TimeSpan 小时计算天数

    我有 1 个文本框 用户将在其中输入小时数 目前 如果输入 26 小时 由于 TimeSpan 的 HH 限制 我们会收到错误 该值将存储在 SQL Server 2008 Time 7 字段中 我怎样才能让它识别超过23小时 不能选择将其
  • 127.0.0.1、0.0.0.0 和 localhost 有什么不同?

    我不明白这些术语之间的区别以及它们之间的联系 我查看了计算机上的主机文件 可以看到 127 0 0 1 和 localhost 已连接 但不确定如何连接 也不知道 0 0 0 0 适合所有这些 我已经看到了这个问题的其他答案 但我是新手 关
  • 为什么 Eclipse 以红色突出显示我的代码以及如何将其关闭? [复制]

    这个问题在这里已经有答案了 为什么 Eclipse 以红色突出显示我的代码以及如何将其关闭 版本 光子发布 4 8 0 这是由于代码覆盖率而被激活的 如果您想删除它 请按照以下步骤操作 转到 Windows gt 显示视图 gt 覆盖范围
  • 带模板的 N 维嵌套金属循环

    我正在尝试使用模板元编程进行 N 维嵌套金属循环 嵌套部分很简单 但是将所有任意数量的迭代索引作为模板参数传递到最内层循环似乎有问题 一个简单的未嵌套的金属环看起来像 template