Overloaded function as argument of variadic template function

后端 未结 2 1189
小蘑菇
小蘑菇 2021-01-12 12:02

I\'m trying to make variadic template function, which takes as arguments overloaded function and its arguments :)

int sumall(int a) { return a; }
int sumall(         


        
2条回答
  •  北恋
    北恋 (楼主)
    2021-01-12 12:54

    (If you're prepared to use variadic macros, then scroll to the end of this answer to see a better answer which make everything fully variadic. But I think that variadic macros are just a g++ extension.)

    It can be made to work, if you're prepared to put the name of the function at the end of the parameter list. By putting it later, the compiler can deduce the necessary types from the earlier parameters to doit:

    cout << doit(7, 6, sumall) << endl;
    cout << doit(10, sumall) << endl;
    

    Here is a demo on ideone.

    The downside is that you have to implement one doit for each number of parameters. I've only implemented it for one- and two- parameter functions, but it shouldn't be a problem to extend this:

    int sumall(int a) { return a; }
    int sumall(int a, int b) { return a+b; }
    
    template
    auto doit( A1 a1, A2 a2, R (*f) (A1,A2)) -> R {
        return f(a1, a2);
    }
    template
    auto doit( A1 a1, R (*f) (A1)) -> R {
        return f(a1);
    }
    

    Update: Sometimes, it might appear that you can get away with having f as the first argument. But that's not as robust as putting it at the end. Consider the example where where are two functions that take the same number of arguments, but different types of parameters. e.g.:

    int sumall(int a, int b) { return a+b; }
    string sumall(string a, string b) { return a+" "+b; }
    

    You need to have the function as the last argument, in order that the template deduction can use the type and number of parameters at the start to deduce the types of the arguments. Here's a demo on ideone of function-arg first and function-arg last.

    The only downside with putting the arg at the end is that we can't then use variadic templates - variadic arg packs must be at the end. And you must get the types exactly right - see how I had to use string("hi") instead of simply "hi".

    Using variadic macros to have the best of all worlds

    By implementing doit as a macro, and using variadic macros (a gcc/g++ extension), it is possible to have a fully variadic solution with the function name appearing first. A demo on ideone.

    cout << doit(sumall, 7, 6) << endl;
    cout << doit(sumall, 10) << endl;
    cout << doit(sumall, string("hi"), string("world")) << endl;
    

    By using decltype and a couple of other simple classes, we can use the args provided to deduce the types of the args and then it can use that to select the right method from the overload set and deduce the return type from that.

    template
    struct OverloadResolved {
            template
            static auto static_doit( R (*f) (Args...), Args ... args ) -> R {
                    return f(args...);
            }
    };
    
    template
    auto deduce(Args...) -> OverloadResolved {
            return OverloadResolved();
    }
    
    template
    struct dummy : public T { };
    
    #define doit(f, ...) ( dummy :: static_doit(f, __VA_ARGS__) )
    

    I'm pretty sure this is a safe use of macros, nothing will be evaluated twice (nothing inside decltype actually executes.

提交回复
热议问题