Steve Emmerson
Steve Emmerson

Reputation: 7842

Move-constructing a C++11 thread from a thread object argument created from a callable argument

In a C++11 initialization list, I'm trying to move-construct a thread from a thread object that was created elsewhere and passed to the constructor. I'm also trying to create the thread object using a passed-in callable argument. I'm not having any luck.

The following (invalid) code will, hopefully, indicate what I'm trying to do:

"a.cpp"

#include <map>
#include <thread>

class Thing {
    int i;
};

class Impl {
    class Entry {
        Thing         thing;
        std::thread   thread;

    public:
        template<class F>
        Entry(Thing& thing, F&& func)
            : thing(thing)
            , thread(func) {
        }
    };

    class ThingMap {
        std::map<Thing, Entry> map;

    public:
        template<class F>
        void add(Thing& thing, F&& func) {
            map.emplace({thing, {thing, func}});
        }
    };

    ThingMap things;

    void execute() {
    }

public:
    void activate(Thing& thing) {
        things.add(thing, [this]{execute();});
    }
};

Here are the errors from my g++ 4.8.5 compiler:

$ g++ -std=c++11 -c a.cpp
a.cpp: In instantiation of ‘void Impl::ThingMap::add(Thing&, F&&) [with F = Impl::activate(Thing&)::__lambda0]’:
a.cpp:38:45:   required from here
a.cpp:27:13: error: no matching function for call to ‘std::map<Thing, Impl::Entry>::emplace(<brace-enclosed initializer list>)’
             map.emplace({thing, {thing, func}});
             ^
a.cpp:27:13: note: candidate is:
In file included from /usr/include/c++/4.8.2/map:61:0,
                 from a.cpp:1:
/usr/include/c++/4.8.2/bits/stl_map.h:540:2: note: std::pair<typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator, bool> std::map<_Key, _Tp, _Compare, _Alloc>::emplace(_Args&& ...) [with _Args = {}; _Key = Thing; _Tp = Impl::Entry; _Compare = std::less<Thing>; _Alloc = std::allocator<std::pair<const Thing, Impl::Entry> >; typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator = std::_Rb_tree_iterator<std::pair<const Thing, Impl::Entry> >]
  emplace(_Args&&... __args)
  ^
/usr/include/c++/4.8.2/bits/stl_map.h:540:2: note:   candidate expects 0 arguments, 1 provided
$ 

Any and all suggestions on how to fix the code will be gratefully appreciated. Please leave the structure alone, however, it's there for a reason.

Upvotes: 3

Views: 63

Answers (2)

bartop
bartop

Reputation: 10315

As gcc points out you are using initializer list here. Or at least it interpretes it this way.

map.emplace({thing, {thing, func}});

To fix it just use types explicitly here:

map.emplace(std::make_pair(thing, Entry(thing, std::thread(std::forward<F>(func)))));

Btw notice the added forward function. It allows you to perfectly forward your function type to the thread

Upvotes: 1

Vittorio Romeo
Vittorio Romeo

Reputation: 93384

std::map::emplace is a template function accepting arbitrary objects to construct the key/value pair. You cannot use list-initialization as the compiler will not be able to deduce the type.

Specify the types instead:

template<class F>
void add(Thing& thing, F&& func) {
     map.emplace(thing, Entry{thing, std::forward<F>(func)});
}

Upvotes: 4

Related Questions