3

.

Hi :-)

I have the following code : the goal is to return a function that is the sum of other functions, roughly. And to learn about variadic templates.

#include <iostream>

template <typename F>
struct FSum {
    FSum(F f) : f_(f) {} ;
    int operator()() {
        return f_() ;
    }
    F f_ ;
} ;
template <typename F1, typename F2, typename... Frest>
struct FSum {
    FSum(F1 f1, F2 f2, Frest... frest) {
        f_ = f1 ;
        frest_ = FSum<F2, Frest...>(f2, frest...) ;
    }
    int operator()() {
        return f_() + frest_() ;
    }
    F1 f_ ;
    FSum<F2, Frest...> frest_ ;
} ;

struct F1 {
    int operator() () { return 1 ; } ; 
} ;
struct F2 {
    int operator() () { return 2 ; } ; 
} ;
struct F3 {
    int operator() () { return 3 ; } ; 
} ;

int main() {
    F1 f1 ; 
    F2 f2 ;
    F3 f3 ;
    FSum<F1, F2, F3> fsum = FSum<F1, F2, F3>(f1, f2, f3) ;
    std::cout << fsum() << std::endl ;
}

But I got the following error message from clang (g++ also gives an error) :

test_variadic.cpp:14:1: error: too many template parameters in template redeclaration template ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

And well I don't understand. I though the compiler would choose the class depending on the number of template parameters ? Since the first one has exactly one and the other one has 2 or more.

Any idea ?

Thanks a lot :-)

Shawn
  • 593
  • 4
  • 12

2 Answers2

4

The compiler is complaining because you are redeclaring the same template struct twice, and what you need is Partial Template Specialization. See the syntax in the example below.

As for the logic to get the variadic template to do what you want, it helps to think of it in terms of recursion. The following code does what you want:

#include <iostream>

using namespace std;

// stops "recursion" when it is called with no parameters
template <typename... Frest> struct FSum {
    int operator()() {
        return 0;
    }
};

// Partial Specialization of FSum above
// called when there is at least one template parameter
template <typename F, typename... Frest>
struct FSum<F, Frest...> {

    // "recursion" in the construction of frest_
    FSum(F f, Frest... frest) : f_(f), frest_(frest...) {}

    // "recursion" in the calling of frest()
    int operator()() {
        return f_() + frest_();
    }

    F f_;
    FSum<Frest...> frest_;
};


struct F1 {
    int operator()() { return 1; }
};

struct F2 {
    int operator()() { return 2; }
};

struct F3 {
    int operator()() { return 3; }
};

int main() {
    F1 f1;
    F2 f2;
    F3 f3;
    FSum<F1, F2, F3> fsum(f1, f2, f3);
    cout << fsum() << endl;
}

Note that I use the word "recursion," but as I understand it, there is no recursion. Instead, there is a sequence of function calls generated at compile time.

Luis Guzman
  • 996
  • 5
  • 8
  • Just to note, there would be also a runtime nested "recursion"-like call stack and you pay all costs of regular runtime recursion! In return f_() + frest_(); where frest_() would invoke the nested call to FSum operator () that would call stripped version of another FSum operator() and so on untill it reach the recursion basis. I don't know whether the compiler is able to roll out this callstack to flat sequence of operators() evaluation or not. – barney Jul 21 '17 at 11:45
  • You are right @barney. The "sequence of function calls" would have a runtime cost equivalent to runtime recursion. – Luis Guzman Jul 25 '17 at 00:56
2

You can't have 2 types with different template parameters. You are trying to use classes as functions (in which you can share multiple definitions/names) but classes and structs don't work this way. You need to rename one of these structs or work them both into one.

Jacob H
  • 864
  • 1
  • 10
  • 25