3

So, I'm fairly new to the C++11 concurrent programming functionality provided by the STL and I was playing around with the following code:

    #include <iostream>
    #include <thread>
    #include <mutex>
    #include <list>

    using namespace std;

    template <typename T>
    class Container
    {
        private:
            mutex mu;
            list<T> myList;

        public:
            void add(T element)
            {
                lock_guard<mutex> lock1(mu);
                myList.emplace_back(element);
            }
            void remove()
            {
                lock_guard<mutex>lock2(mu);
                myList.pop_back();
            }
            void print()
            {
                for(const auto & element : myList)
                {
                    cout << element << endl;
                }
            }
    };

    int main()
    {
        Container<int> c;

        thread t1(&Container<int>::add, c, 5); //ERROR
        thread t2(&Container<int>::add, c, 10); //ERROR

        thread t4(&Container<int>::remove, c); //ERROR
        thread t5(&Container<int>::remove, c); //ERROR

        t1.join();
        t2.join();
        t4.join();
        t5.join();

        c.print();
    }

When I try to compile my code, the lines I've marked "ERROR" caused the compiler to tell me:

error: call to implicitly-deleted copy constructor of
  'typename decay<Container<int> &>::type' (aka 'Container<int>')
return _VSTD::forward<_Tp>(__t);

error: no matching constructor for initialization of
  '__tuple_leaf<1UL, Container<int> >'
        __tuple_leaf<_Uf, _Tf>(_VSTD::forward<_Up>(__u))...,

error: no matching function for call to '__decay_copy'
                            __decay_copy(_VSTD::forward<_Args>(__args))...));
                            ^~~~~~~~~~~~

Now, I've looked at this question and this question while I was writing my code, but I'm still missing some small detail. If someone could provide some help, that would be brilliant. Thanks!

Community
  • 1
  • 1
busebd12
  • 997
  • 2
  • 12
  • 24

1 Answers1

2

A thread needs to make a copy of all of its arguments and your Container is non copyable. It is non-copyable because one of its members (the std::mutex) is non-copyable. The solution to this is rather than give the thread c directly is to give it something that it can make a copy of.

That is:

    thread t1(&Container<int>::add, &c, 5);

The following should work as well, but may not (see T.C.'s comment):

    thread t2(&Container<int>::add, std::ref(c), 10);

Note that it's a good thing that this didn't compile for you, because otherwise your threads would be performing work on various copies of your container - rather than just the one as you likely expected.

Community
  • 1
  • 1
Barry
  • 286,269
  • 29
  • 621
  • 977
  • The second version depends on [LWG 2219](http://cplusplus.github.io/LWG/lwg-active.html#2219) and so probably won't compile on current implementations. – T.C. May 18 '15 at 03:27
  • @Barry: I made the changes you suggested in your answer, but the compiler is now saying error: attempt to use a deleted function __invoke(_VSTD::move(_VSTD::get<0>(__t)), _VSTD::move(_VSTD::get<_Indices>(__t))...); – busebd12 May 18 '15 at 03:30
  • @xXAnointedXx Which one, the first or the second? – Barry May 18 '15 at 03:33
  • @Barry: thread t2(&Container::add, ref(c),10); and thread t4(&Container::remove, ref(c)); – busebd12 May 18 '15 at 03:35
  • @Berry: should I give both a pointer? And what do you mean exactly when you say "pointer"? – busebd12 May 18 '15 at 03:39
  • 1
    @SneakyPolarBear Huh? The problem is that OP was trying to _copy_ the container, but it's non-copyable. It also happens to be non-movable, but neither of those matters since OP didn't intend on either copying or moving to begin with. – Barry Sep 25 '19 at 14:54