Rubin
Rubin

Reputation: 332

Template deduction complaints ambiguous candidates

I intend to implement the multiplication operator of my "Sparse Vector" and "Vector" classes. The following simplified code demo shows my problem

The Vector class in Vector.hpp

#pragma once

template <typename T>
class Vector 
{
public:
    Vector() {}

    template <typename Scalar>
    friend Vector operator*(const Scalar &a, const Vector &rhs)     // #1
    {
        return Vector();
    }
};

The Sparse Vector class in SpVec.hpp

#pragma once
#include "Vector.hpp"

template <typename T>
class SpVec 
{
public:
    SpVec() {}

    template <typename U>
    inline friend double operator*(const SpVec &spv, const Vector<U> &v)   // #2
    {
        return 0.0;
    }
};

The test code in main.cpp:

#include "Vector.hpp"
#include "SpVec.hpp"


#include <iostream>

int main() 
{
    Vector<double> v;

    SpVec<double> spv;

    std::cout << spv * v;
    return 0;
}

I build the test program with

g++ main.cpp -o test

which gives the ambiguous template deduction error

main.cpp: In function ‘int main()’:
main.cpp:13:26: error: ambiguous overload for ‘operator*’ (operand types are ‘SpVec<double>’ and ‘Vector<double>’)
        std::cout << spv * v;
                    ~~~~^~~
In file included from main.cpp:2:0:
SpVec.hpp:12:26: note: candidate: double operator*(const SpVec<T>&, const Vector<U>&) [with U = double; T = double]
    inline friend double operator*(const SpVec &spv, const Vector<U> &v)   // #2
                        ^~~~~~~~
In file included from main.cpp:1:0:
Vector.hpp:10:19: note: candidate: Vector<T> operator*(const Scalar&, const Vector<T>&) [with Scalar = SpVec<double>; T = double]
    friend Vector operator*(const Scalar &a, const Vector &rhs)     // #1

I expect the #2 method definition is more close to my calling.

Please help me understand how the ambiguous error comes out and how to resolve the issue.

Upvotes: 6

Views: 150

Answers (2)

Rubin
Rubin

Reputation: 332

I come up with another idea that the prior type information Scalar can be used with the SFAINE feature enabled by the c++11 standard library struct std::enable_if.

The codes:

Vector.hpp

#pragma once

#include <iostream>
#include <type_traits>

template <typename T>
class Vector
{
public:
    Vector() {}

    template <typename Scalar>
    typename std::enable_if<std::is_arithmetic<Scalar>::value, Vector<T>>::type
    operator*(const Scalar &rhs) const// #1
    {
        std::cout << "Vector * Scalar called." << std::endl;
        return Vector();
    }

    template <typename Scalar>
    inline friend typename std::enable_if<std::is_arithmetic<Scalar>::value, Vector<T>>::type
    operator*(const Scalar &lhs, const Vector &rhs)
    {
        std::cout << "Scalar * Vector called." << std::endl;
        return Vector();
    }
};

SpVec.hpp

#pragma once
#include "Vector.hpp"

#include <iostream>

template <typename T>
class SpVec
{
public:
    SpVec() {}

    template <typename U>
    inline double operator*(const Vector<U> &rhs) const // #2 as member function
    {
        std::cout << "SpVec * Vector called" << std::endl;
        return 0.0;
    }

    template <typename U>
    inline friend double operator*(const Vector<U> &lhs, const SpVec &rhs)
    {
        std::cout << "Vector * SpVec called" << std::endl;
        return 0.0;
    }
};

main.cpp

#include "SpVec.hpp"
#include "Vector.hpp"

#include <iostream>

int main()
{
    Vector<double> v;
    SpVec<double> spv;

    double a = spv * v;
    a = v * spv;

    Vector<double> vt;
    vt = v * 2.0;
    vt = 2.0 * v;

    return 0;
}

Build the program with c++11

g++ -std=c++11 main.cpp -o test

The result:

SpVec * Vector called.
Vector * SpVec called.
Vector * Scalar called.
Scalar * Vector called.

Upvotes: 3

Gaurav Sehgal
Gaurav Sehgal

Reputation: 7542

The argument to operator* are SpVec<double> and Vector<double>. It could be resolved to

operator*(const Scalar &a, const Vector &rhs) with scalar as SpVec<double> and rhs as Vector<double>.

It could also resolve to

operator*(const SpVec &spv, const Vector<U> &v) with spv as SpVec<double> and U as double.

One way of resolving this is to turn Vector::operator* to non-friend function.

Vector operator*(const Scalar &a)     // #1
{
    //The other argument here will be accessed using this pointer.
    return Vector();
}

and you can call it as

int main() 
{
   Vector<double> v;
   SpVec<double> spv;
   std::cout << spv * v; // will call #2
   v * spv;              //will call #1
   return 0;
}

Upvotes: 0

Related Questions