简体   繁体   中英

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......)>
{
    // ... ... ...
};

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM