6
votes

I am trying to overload a multiplication operator but do not want to type out multiple overloaded functions to take into account multiplying int and float, int and double, float and int, etc... I was hoping to write one overloaded operator to account for all combinations of multiplication with floats, ints, and doubles and get the proper return type. I am getting errors saying that no operator found which takes a right-hand operand of type 'Widget::Widget' (or there is no acceptable conversion). I think this is because I am using the decltype to set the template type of the return object, the Widget. Using a trailing return type works if the return is not a template object.

Here is an example of the overloaded operator I am trying to make:

template<typename T1, typename T2>
auto
operator*(const Widget<T1>& aWidge, const Widget<T2>& bWidge) -> Widget<decltype(aWidge.x*bWidge.x)>
{
    Widget<decltype(aWidge.x*bWidge.x)> result;

    //do stuff needed when multiplying two Widgets

    return result;
}

template<typename T>
Widget<T>& Widget<T>::operator=(const Widget<T>& aWidget)
{
    x = aWidget.x;

    return *this;
}

And here is an example of the template class

template<typename T> class Widget
{
    private:
        T x;
    public:
        Widget();
        ~Widget();
        void SetX(T value);
        Widget<T>& operator=(const Widget<T>& aWidget);
}

Example Main.cpp

int main()
{
    Widget<int> aWidge;
    Widget<float> bWidge;
    Widget<float> cWidge;

    aWidge.SetX(2);
    bWidge.SetX(2.0);

    cWidge = aWidge*bWidge; //this should give a float return type
}
2
I'd start by fixing your operator = for Widget<T> to properly resolve the member operator, i.e. Widget<T>& Widget<T>::operator=(const Widget<T>& aWidget)WhozCraig
Did you try to compile? If so, what happened?R Sahu
That's a template argument, not a template parameter.Lightness Races in Orbit
@user3390212: Don't miss the point. The more typos you make, the more of our time you waste. Get it right first, then post and ask for help with it. Also drop the entitled attitude, your highness.Lightness Races in Orbit
It works for me (after I fix the obvious problems: define operator= properly, use declval<T1>() (etc.) instead of aWidge.x to get around private variable access, define/default the missing functions). Perhaps you can post a minimal compilable example that illustrates the problem, along with the compiler, version, and platform you're on.Adam H. Peterson

2 Answers

5
votes

Reading the error message carefully, the problem is obvious:

candidate template ignored: substitution failure [with T1 = int, T2 = float]: 'x' is
      a private member of 'Widget<int>'

Non-member binary operator* is trying to access private member x in its declaration (and definition). Since you have a setter function, a plain solution is to also define a getter and only access member x through this function:

template<typename T> class Widget
{
    private:
        T x;

    public:
        Widget() {}
        ~Widget() {}
        void SetX(T value) {}
        T& GetX() { return x; }
        const T& GetX() const { return x; }
        Widget<T>& operator=(const Widget<T>& aWidget);
};

template<typename T1, typename T2>
auto
operator*(const Widget<T1>& aWidge, const Widget<T2>& bWidge)
-> Widget<decltype(aWidge.GetX()*bWidge.GetX())>
{
    Widget<decltype(aWidge.GetX()*bWidge.GetX())> result;
    //...
    return result;
}

Another option would be to make operator* a friend:

template<typename T> class Widget
{
    private:
        T x;

    template<typename T1, typename T2>
    friend auto
    operator*(const Widget<T1>& aWidge, const Widget<T2>& bWidge)
    -> Widget<decltype(aWidge.x*bWidge.x)>;

    public:
        Widget() {}
        ~Widget() {}
        void SetX(T value) {}
        Widget<T>& operator=(const Widget<T>& aWidget);
};

template<typename T1, typename T2>
auto
operator*(const Widget<T1>& aWidge, const Widget<T2>& bWidge)
-> Widget<decltype(aWidge.x*bWidge.x)>
{
    Widget<decltype(aWidge.x*bWidge.x)> result;

    return result;
}

or, make it a member function (thanks WhozCraig).

You will probably also need

typename std::decay<decltype(aWidge.x*bWidge.x)>::type

instead of just decltype(aWidge.x*bWidge.x).

Other options are

typename std::decay<decltype(std::declval<T1>()*std::declval<T2>())>::type

which bypasses the previous problem entirely (thanks Adam), or just

typename std::common_type<T1, T2>::type

which should fit for this purpose and is arguably the simplest form.

3
votes

Visual Studio 2012

Don't mind the sloppy code. It was a quick fix for code that didn't compile properly to begin with (nevermind the auto decltype problem).

template<typename T>
class Widget
{
public:
    T x;
public:
    Widget()
        : x(666)
    {}

    ~Widget() {}

    void SetX(T value)
    {
        x = value;
    }

    Widget<T>& operator=(const Widget<T>& aWidget)
    {
        x = aWidget.x;

        return *this;
    }
};


template<typename T1, typename T2>
auto operator*(const Widget<T1>& aWidge, const Widget<T2>& bWidge) -> Widget<typename std::remove_const<decltype(aWidge.x*bWidge.x)>::type>
{
    Widget<typename std::remove_const<decltype(aWidge.x*bWidge.x)>::type> result;

    result.x = aWidge.x * bWidge.x;

    return result;
}



int main ()
{
    Widget<int> aWidge;
    Widget<float> bWidge;
    Widget<float> cWidge;

    aWidge.SetX(2);
    bWidge.SetX(2.0);

    cWidge = aWidge*bWidge; //this should give a float return type
}