12

Premise #1: I have already solved the error, but I didn't deeply understand the cause of the compiler error.

Premise #2: The goal of this program is to copy a image into another image by a multithreaded process. Maybe a better way exists, but this is not the focus topic of the question (see premise #1).

I wrote a simple program using OpenCV 3.1 library to copy a image into another image. It takes advantage of all cores of the CPU using more threads.

The code is:

#include <opencv2/opencv.hpp>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <thread>

using namespace cv;
using namespace std;

#define IMG_PATH "..\\img\\50a.png"


void copy_image(const Mat& input, Mat& output, int row_offset, int skip_row)
{
    cout << row_offset;
    Size size = input.size();
    for (int r = row_offset; r < size.height; r += skip_row)
    {
        for (int c = 0; c < size.width; c++)
        {
            output.at<Vec3b>(r, c) = input.at<Vec3b>(r, c);
        }
    }
}

int main()
{
    Mat input_img = imread(IMG_PATH);
    Mat output_img = Mat(input_img.size(), input_img.type()); output_img.setTo(255);

    vector<thread> threads;
    int thread_number = thread::hardware_concurrency();

    for (int i = 0; i < thread_number; i++)
    {
        cout << i;
        auto var = [=]() 
        {
            return copy_image(input_img, output_img, i, thread_number);
        };

        threads.push_back(thread(var));
    }

    for (auto& thread : threads) 
        thread.join();

    imshow("output_img", output_img);
    imwrite("result.png", output_img);
    waitKey(0);
}

The compiler returns me this error

Error C2664 'void copy_image(const cv::Mat &,cv::Mat &,int,int)': cannot convert argument 2 from 'const cv::Mat' to 'cv::Mat &'

that it reffers this line of code:

return copy_image(input_img, output_img, i, thread_number);

I solved this error replacing this line

auto var = [=]()

with this

auto var = [=, &input_img, &output_img]() 

but actually I don't deeply understand why I received that error.

Radioga
  • 416
  • 1
  • 5
  • 16
  • I wish this was the default behaviour for _all_ objects defined in a program: `const` by default, add `mutable` to change it. – isanae Aug 21 '16 at 16:49

2 Answers2

13

If you do a capture by value in a lambda, you will get a 'member' which gets stored. As the default operator() is a const function, you cannot modify them.

Lambdas can be defined as []() mutable {} to allow you to modify the local variables.

By capturing the value by reference, you have something which behaves like a const pointer to a non-const object, so without the mutable, you can adapt those objects. (Unless they already where const)

JVApen
  • 11,008
  • 5
  • 31
  • 67
  • 5
    Plus one because you illustrate the real reason: that is, captured by copy values are not stored as `const` but rather the call `operator()` is `const` member function unless `lambda` is defined as `mutable`. – 101010 Aug 21 '16 at 14:09
9

Captured variables are indeed const in the scope of the lamba:

[foo]()
{
   // foo, whatever that is, is const
}

In a mutable lambda, the captured variables are not constant:

[foo]()
mutable {

   // Lambda can modify foo, but it's a copy of the original
   // captured variable
}
Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • Worth noting that the object copied there( an OpenCV matrix), is a shared pointer to the actual data. A mutable lambda would still modify the underlying data. – Sam Aug 21 '16 at 17:59