Search code examples
c++templatesvariadic-templatestemplate-meta-programmingtemplate-specialization

C++ template ambiguous instantiation


I am trying to do some interpolation using templates but I get an "ambiguous template instantiation" error. Here is the code

// interpolation rules
enum InterRule {trap, rect, trapSum};

// Rectangle rule
template <int n, int k, InterRule rule, class Expr> struct Integration {
    static double integrate(double a, double b){
        return (b-a)/n * Expr::eval(a + (k-1)*(b-a)/n) + Integration<n, k - 1, rule, Expr>::integrate(a,b);
    }
};

// termination case
template <int n, InterRule rule, class Expr> struct Integration<n,0,rule,Expr> {
    static double integrate(double a, double b){
        return 0;
    }
};

// Trapezoidal rule
template <int n, int k, class Expr> struct Integration<n, k, trap, Expr> {
    static double integrate(double a, double b){
        return (b-a)/n * (Expr::eval(a)/2 + Integration<n,k-1,trapSum,Expr>::integrate(a,b) + Expr::eval(b)/2);
    }
};

// Trapezoidal sum
template <int n, int k, class Expr> struct Integration<n, k, trapSum, Expr> {
    static double integrate(double a, double b){
        return Expr::eval(a + k*(b-a)/n) + Integration<n,k-1,trapSum,Expr>::integrate(a,b);
    }
};

Basically, I am trying to implement the Trapezoidal rule so that it is statically unrolled. enter image description here However, it appears that the compiler gets confused whether to use "termination case" or "trapezoidal sum". What am I doing wrong and is there a workaround? I want to force it to use "termination case" if k==0 regardless of the type of InterRule rule.

EDIT Additional code to make it run:

// the types of expressions (+,-,*, etc.)
enum ExprType { mul, divide, add, sub, constant};

// constant
template <ExprType eType, class Left, class Right, int coeff, int power> struct Expr {
    static double eval(double x){
        return coeff * std::pow(x, power);
    }
};


int main()
{

    double a = 1;
    double b = 2;

    // Expr defines the function f(x) = x
    Integration<50, 50, trap, Expr<constant,int,int,1,1>> inte2;
    std::cout << inte2.integrate(a,b) << std::endl;

    return 0;
}

Solution

  • You can try to disambiguate adding an additional template parameter.

    Maybe something as

    // .................................................VVVVVVVVVVV
    template <int n, int k, InterRule rule, class Expr, bool = true>
    struct Integration {
       // ...
    };
    

    The ground case explicitly with false

    template <int n, InterRule rule, class Expr>
    struct Integration<n, 0, rule, Expr, false> { // <--- false !
        static double integrate(double a, double b){
            return 0;
        }
    };
    

    and adding the right parameter in recursion calls

        return (b-a)/n * Expr::eval(a + (k-1)*(b-a)/n)
           + Integration<n, k - 1, rule, Expr, (k>1)>::integrate(a,b);
        // ....................................^^^^^
    

    The following is a full compiling example

    #include <cmath>
    #include <iostream>
    
    // interpolation rules
    enum InterRule {trap, rect, trapSum};
    
    // Rectangle rule
    template <int n, int k, InterRule rule, class Expr, bool = true>
    struct Integration {
        static double integrate(double a, double b){
            return (b-a)/n * Expr::eval(a + (k-1)*(b-a)/n)
               + Integration<n, k - 1, rule, Expr, (k>1)>::integrate(a,b);
        }
    };
    
    // termination case
    template <int n, InterRule rule, class Expr>
    struct Integration<n, 0, rule, Expr, false> {
        static double integrate(double a, double b){
            return 0;
        }
    };
    
    // Trapezoidal rule
    template <int n, int k, class Expr>
    struct Integration<n, k, trap, Expr> {
        static double integrate(double a, double b){
            return (b-a)/n * (Expr::eval(a)/2
               + Integration<n,k-1,trapSum,Expr,(k>1)>::integrate(a,b)
               + Expr::eval(b)/2);
        }
    };
    
    // Trapezoidal sum
    template <int n, int k, class Expr>
    struct Integration<n, k, trapSum, Expr> {
        static double integrate(double a, double b){
            return Expr::eval(a + k*(b-a)/n)
               + Integration<n,k-1,trapSum,Expr,(k>1)>::integrate(a,b);
        }
    };
    
    // the types of expressions (+,-,*, etc.)
    enum ExprType { mul, divide, add, sub, constant};
    
    // constant
    template <ExprType eType, class Left, class Right, int coeff, int power> struct Expr {
        static double eval(double x){
            return coeff * std::pow(x, power);
        }
    };
    
    
    int main ()
     {
    
        double a = 1;
        double b = 2;
    
        // Expr defines the function f(x) = x
        Integration<50, 50, trap, Expr<constant,int,int,1,1>> inte2;
        std::cout << inte2.integrate(a,b) << std::endl;
     }