constructing a std::function object from a type with a variadic template constructor

69 views Asked by At

I have a templated class which instantiates an internal object of the template type and its constructor forwards the arguments to the internal object.

template<typename T>
struct B
{
    template<typename... Args>
    B(Args&&... args) : m(std::forward<Args>(args)...) {}
    
    B(const B& b) = default;
    B(B&& b) noexcept = default;

    void operator()() { m(); }
    
    T m;
};

Also this class is callable and it simply delegates the call to its internal member. The reason it is callable is because I want to pass it around as a std::function. The problem is that the compiler doesn't let me initialize a std::function with an object of type B:

struct A
{
    void operator()() { std::cout << "A()\n"; }
};

void foo()
{
    B<A> b;
    std::function<void()> fb = b; //Error
}

The error is a cryptic one:

error: no matching function for call to ‘A::A(B&)’
     B(Args&&... args) : a(std::forward<Args>(args)...) {}

Nowhere in my code have I made a call to A::A(B&). I expect std::function to call B::B(B&) constructor to copy the data from b to fb.

Note that this only happens because of the variadic template constructor of B. The following code compiles fine:

A a;
std::function<void()> fa = a;
1

There are 1 answers

2
Caleth On BEST ANSWER

The instantion of

template<typename... Args>
B(Args&&... args)

with Args... == B& is a better match for copying a non-const lvalue B than either your copy or move constructor. Instantiating that is like

B(B& args) : m(args) {}

which uses A::A(B&)

You could either add an overload B(B&) = default;, or add some SFINAE to your template constructor.