Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does C++11 not support 'std::function<void(int, ...)>'?

#include <functional>

void f1(int)
{}

void f2(int, ...)
{}

int main()
{
    std::function<void(int)>      g1 = f1; // OK.
    std::function<void(int, ...)> g2 = f2; // Error! Not standard C++!
}

Why does C++11 not provide a specialized template class as follows:

template<class ResultType, class... ArgTypes>
class function<ResultType(ArgTypes......)>
{
    // ... ... ...
};
like image 503
xmllmx Avatar asked Feb 19 '13 01:02

xmllmx


1 Answers

I do not mean to provide the ultimate reason why that specialization is not provided (I do not know that), but maybe I could hint at some of the technical obstacles that could be encountered when trying to implement it. That will hopefully give you a feeling of why the specialization is not there.

Let's consider first how the std::function<> class template itself could be implemented. The type erasure technique that underlies its design could be sketched as follows (this is just an illustrative simplification, the real implementation is way more complex):

#include <memory>

template<typename T>
struct function { };

template<typename R, typename... Args>
struct function<R(Args...)>
{

public:

    template<typename F>
    function(F&& f) : _holder(
        new holder<typename std::decay<F>::type>(std::forward<F>(f))
        )
    { }

    R operator () (Args&&... args)
    { _holder->call(std::forward<Args>(args)...); }

private:

    struct holder_base
    { virtual R call(Args&&... args) = 0; };

    template<typename F>
    struct holder : holder_base
    {
        holder(F&& f) : _f(std::forward<F>(f)) { }
        R call(Args&&... args) { return _f(std::forward<Args>(args)...); }
        F _f;
    };

    std::unique_ptr<holder_base> _holder;
};

Now let's see how the specialization for ellipses would look like. First of all, the number and type of the arguments provided to a variadic function is not fixed in that function's signature. Therefore, the call operator of our specialized template must be a function template accepting any number and type of arguments:

template<typename R, typename... Args>
struct function<R(Args.......)>
{
    ...

    template<typename... Ts>
    R operator () (Args&&... args, Ts&&... ts)
    { _holder->call(std::forward<Args>(args)..., std::forward<Ts>(ts)...); }

    ...

This forces us, in turn, to make holder<>'s call operator a variadic function template. However, in order to realize type erasure, that same call operator must be virtual, and function templates cannot be virtual in C++.

Things would certainly be easier if variadic arguments (I'm talking about ellipses here) could be easily forwarded without having to recur to variadic template parameters and perfect forwarding. I am not aware of a simple way to achieve that though, and especially not if no other argument is to be passed to the function than those matching the variadic list.

like image 50
Andy Prowl Avatar answered Sep 30 '22 22:09

Andy Prowl