如何有条件地定义lambda?

时间:2019-04-16 12:32:27

标签: c++

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

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

不应该在循环之内。

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

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

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

enter image description here

问题

如何有条件地定义这种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;
    }
}

2 个答案:

答案 0 :(得分:10)

  

我的带有此lambda的代码无法使用以下红色波浪形文字进行编译

您不能在没有捕获的情况下在lambda表达式主体内使用randomRow,因为生成的闭包对象需要访问它。

即使您使用[&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); }, /* ... */)
    }
}

答案 1 :(得分:3)

之所以失败,是因为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)。我在上面的代码中使用了通用的按引用捕获[&]来做到这一点。