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
}
A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)
Template non-type arguments in C++ It is also possible to use non-type arguments (basic/derived data types) i.e., in addition to the type argument T, it can also use other arguments such as strings, function names, constant expressions, and built-in data types.
In the C++ programming language, decltype is a keyword used to query the type of an expression. Introduced in C++11, its primary intended use is in generic programming, where it is often difficult, or even impossible, to express types that depend on template parameters.
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.
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With