Вопрос

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

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
}

Другие советы

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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top