【问题标题】:How to conditionally define a lambda?如何有条件地定义一个 lambda?
【发布时间】:2019-09-06 13:27:22
【问题描述】:

以下函数将在加载的图像上随机“撒盐”。为了提高性能,条件语句

uint j = rows == 1 ? 0 : randomRow(generator);

不应在循环内。

相反,我想在循环之前将 lambda getJ 定义为

auto getJ = rows == 1 ? []() {return 0; } : []() {return randomRow(generator); };

但是,我使用此 lambda 的代码无法使用以下红色波浪状文本进行编译:

问题

如何有条件地定义这样一个 lambda?

void salt_(Mat mat, unsigned long long n)
{
    const uchar channels = mat.channels();
    uint cols = mat.cols;
    uint rows = mat.rows;

    if (mat.isContinuous())
    {
        cols *= rows;
        rows = 1;
    }

    default_random_engine generator;
    uniform_int_distribution<uint> randomRow(0, rows - 1);
    uniform_int_distribution<uint> randomCol(0, cols - 1);



    // auto getJ = rows == 1 ? []() {return 0; } : []() {return randomRow(generator); };


    uchar * const data = mat.data;

    for (unsigned long long counter = 0; counter < n; counter++)
    {
        uint i = randomCol(generator);
        uint j = rows == 1 ? 0 : randomRow(generator);
        //uint j = getJ();

        uint index = channels * (cols * j + i);
        for (uchar k = 0; k < channels; k++)
            data[index + k] = 255;
    }
}

【问题讨论】:

  • “我的 lambda 代码无法编译” - 你得到什么错误?
  • 您试图引用局部变量 generator 而不捕获它。如果第二个 lambda 是无捕获的,它将编译。
  • 似乎是过早的优化。函数调用开销肯定会大于三元开销吗?
  • 有条件的真的是性能问题吗?你分析过代码吗?
  • @Artyer 做得好,可以内联对 lambda 的调用。

标签: c++


【解决方案1】:

我的带有这个 lambda 的代码不能用下面的红色波浪文本编译

如果没有事先捕获它,你不能在 lambda 表达式的主体内使用randomRow,因为生成的闭包对象需要访问它。

即使您使用[&amp;randomRow],代码仍然无法编译,因为每个 lambda 表达式都会产生 unique 类型的闭包,即使 lambda 表达式完全相同。

您可以转而解决问题以避免任何开销并实现您想要的 - 创建一个接受您要调用的 lambda 的函数:

template <typename F>
void saltImpl(F&& getJ, /* ... */)
{
    uchar * const data = mat.data;

    for (unsigned long long counter = 0; counter < n; counter++)
    {
        uint i = randomCol(generator);
        uint j = rows == 1 ? 0 : randomRow(generator);
        //uint j = getJ();

        uint index = channels * (cols * j + i);
        for (uchar k = 0; k < channels; k++)
            data[index + k] = 255;
    }
}

使用示例:

void salt_(Mat mat, unsigned long long n)
{
    const uchar channels = mat.channels();
    uint cols = mat.cols;
    uint rows = mat.rows;

    if (mat.isContinuous())
    {
        cols *= rows;
        rows = 1;
    }

    default_random_engine generator;
    uniform_int_distribution<uint> randomRow(0, rows - 1);
    uniform_int_distribution<uint> randomCol(0, cols - 1);

    if (rows == 1)
    {
        saltImpl([]{ return 0; }, /* ... */);
    }
    else
    {
        saltImpl([&]{ return randomRow(generator); }, /* ... */)
    }
}

【讨论】:

  • 为什么不写一个接受 lambda 的 lambda?
【解决方案2】:

为什么会失败是因为 lambda 是不同的类型。这很自然,他们的operator() 有不同的定义。这意味着您希望您的以下代码适用于两种不同的类型。而 C++ 使代码适用于不同类型的方法是使用模板。

使用getJ 将代码转换为函数模板(它可以是您的实现文件的本地),如下所示:

template <class G>
void salt_impl_(Mat mat, unsigned long long n, default_random_engine &generator, G getJ)
{
    const uchar channels = mat.channels();
    uint cols = mat.cols;
    uint rows = mat.rows;

    if (mat.isContinuous())
    {
        cols *= rows;
        rows = 1;
    }

    uchar * const data = mat.data;

    uniform_int_distribution<uint> randomCol(0, cols - 1);

    for (unsigned long long counter = 0; counter < n; counter++)
    {
        uint i = randomCol(generator);
        uint j = getJ();

        uint index = channels * (cols * j + i);
        for (uchar k = 0; k < channels; k++)
            data[index + k] = 255;
    }
}


void salt_(Mat mat, unsigned long long n)
{
    const uchar channels = mat.channels();
    uint cols = mat.cols;
    uint rows = mat.rows;

    if (mat.isContinuous())
    {
        cols *= rows;
        rows = 1;
    }

    default_random_engine generator;
    uniform_int_distribution<uint> randomRow(0, rows - 1);

    if (rows == 1)
      salt_impl_(mat, n, generator, []() {return 0; });
    else
      salt_impl_(mat, n, generator, [&]() {return randomRow(generator); });
}

通过传递更多参数、使它们成为类的成员或类似的东西,随意减少函数和模板之间的初始部分重复。

还要注意,非平凡的 lambda 必须捕获它访问的变量(randomRowgenerator)。我在上面的代码中使用了通用的引用捕获[&amp;]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-19
    • 2022-01-14
    • 2017-06-30
    • 2010-12-02
    • 1970-01-01
    • 2022-01-13
    • 1970-01-01
    • 2019-07-16
    相关资源
    最近更新 更多