ambiguous call on overloaded variadic template operators

Hello!

When compiling the code

1
2
3
4
5
6
7
8
9
#include "tensor.h"

int main()
{
    Tensor<2,-2> m = {{1,2},{1,3}};
    Tensor<2> v = {1,5};
    std::cout<<m*v<<"\n";
    return 0;
}

Under gcc -std=c++11 I get the following error

error: ambiguous overload for 'operator*' (operand types are 'Tensor<2, -2>' and 'Tensor<2>')
candidates are:
Field operator*(const Field&, const Field&)
Tensor<N, dims ...> operator*(const Tensor<N, dims ...>&, const Field&) [with int N = 2; int ...dims = {-2}]
Tensor<N, dims ...> operator*(const Field&, const Tensor<N, dims ...>&) [with int N = 2; int ...dims = {}]

I have two classes Field and Tensor, with the following structure, where I have left out unimportant parts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//field.h

    class Field
    {
        std::complex<long double> _value;
    public:
            Field(const Field &);
        template<typename S1 = long double,typename S2 = long double>
            Field(const S1 & = S1(),const S2 & = S2());
        template<typename S>
            Field(const std::complex<S> &);

            Field & operator = (const Field &);
        template<typename S>
            Field & operator = (const S &);
        template<typename S>
            Field & operator = (const std::complex<S> &);

        template<typename S>
            operator S () const;
        template<typename S>
            operator std::complex<S> () const;
        ...
    };
    ...
    Field operator * (const Field &,const Field &);
    ...

And the Tensor header file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
    //tensor.h

    //class declaration
    template<int... dims>
    struct Tensor;

    //class definition
    template<int N,int... dims>
    struct Tensor<N,dims...>
    {
        bool covariant;
        char index;
        std::vector<Tensor<dims...>> el;

        Tensor();
        Tensor(const Tensor<N,dims...> &);
        Tensor(std::initializer_list<Tensor<dims...>>);
        ...

        Tensor<dims...> & operator [] (int i);
        Tensor<dims...> operator [] (int i) const;
        ...
    };

    //Class definition of template specialization for 0-rank tensors
    template<>
    struct Tensor<> : public Field {using Field::Field;};

    ...
    template<int N,int... dims>
    Tensor<N,dims...> operator * (const Tensor<N,dims...> &,const Field &);

    template<int N,int... dims>
    Tensor<N,dims...> operator * (const Field &,const Tensor<N,dims...> &);

    template<int... dims1,int N,int... dims2>
    Tensor<dims1...,dims2...> operator * (const Tensor<dims1...,-N> &,const Tensor<N,dims2...> &);
    ...


Why do I get an ambiguity and why is not the wanted operator*-overload (the last one in the tensor.h file) not even mentioned as one of the candidates? Is it clear what I want to do? And if so, what can I do to make the call unambiguous?
The error should indicate exactly why a candidate could not be chosen. What compiler are you using?
as the compiler points out, the only candidates are the ones that take a Field as an argument (and they fail overload resolution because there are no suitable conversions).

The reason operator* for two tensors isn't part of the overload set is that it failed template argument deduction and was never instantiated.
The root of the problem is Tensor<dims1..., -N>: packs are greedy and dims1... eats both 2 and -2 from your first Tensor, leaving nothing for the last parameter, resulting in an invalid type (the standardese for this is "non-deduced context"). Try changing that to Tensor<N, dims1...> and it will compile.

This compiles for me:
1
2
3
4
5
6
template<int I, int... Is> struct last_int { static const int value = last_int<Is...>::value; };
template<int I> struct last_int<I> { static const int value = I; };

template<int... dims1, int N, int... dims2,
         class = typename std::enable_if<last_int<dims1...>::value == -N>::type >
Tensor<dims1...,dims2...> operator * (const Tensor<dims1...> &,const Tensor<N,dims2...> &);

PS: oh, you will also have to strip the last int from that dims1... in the returned type, but it shouldn't be any harder
Last edited on
Thanks! Now it's clear how the variadic templates works, the strip function is also useful!
Topic archived. No new replies allowed.