Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 501
Rubin Avatar asked Nov 13 '18 05:11

Rubin


1 Answers

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.
like image 84
Rubin Avatar answered Nov 10 '22 04:11

Rubin