C++: implicit casting in templates, why it does not work? - c++

It seems strange but this simple code works with int instead of T, and does not work with template T.
template <typename T>
class Polynomial {
public:
Polynomial (T i) {}
Polynomial& operator+= (const Polynomial& rhs) {
return *this;
}
};
template <typename T>
const Polynomial<T> operator+ (Polynomial<T> lhs_copy, const Polynomial<T>& rhs) {
return lhs_copy += rhs;
}
Polynomial<int> x (1), y = x + 2; // no match for 'operator+' in 'x + 2'

Implicit conversion don't apply during template argument deduction, you might render your function friend(so that the type is known):
template <typename T>
class Polynomial {
public:
Polynomial (T i) {};
Polynomial& operator+= (const Polynomial& rhs) { return *this; };
friend Polynomial operator+ (Polynomial lhs, const Polynomial& rhs) {
return lhs+=rhs;
}
};
Also related: C++ addition overload ambiguity

Related

Why do implicit conversions not work for out-of-line operators on a template class

I tried writing a class with operators two different ways. First, I tried it with the operators defined inside the class. Then, I tried it with the operators defined outside the class. Defining the operators outside the class appears to be better because I can take advantage of implicit conversions on the left-hand-operand. It appears to be widely recommended that operators be defined outside the class when possible.
However, when I make the class a template class, implicit conversions on the left-hand-operand no longer work. In fact, implicit conversions on the right-hand-operand also do not work. Am I doing something wrong?
namespace N1 {
template <class T>
class C1 {
public:
double value;
/* implicit */ C1(double value) : value{value} {}
C1(const C1<T>& other) : value{other.value} {}
C1<T>& operator=(const C1<T>& other) {
this->value = other.value;
return *this;
}
// Define operator+ for C1 inline
inline C1<T> operator+(const C1<T>& other) const {
return this->value + other.value;
}
};
template <class T>
class C2 {
public:
double value;
/* implicit */ C2(double value) : value{value} {}
C2(const C2<T>& other) : value{other.value} {}
C2<T>& operator=(const C2<T>& other) {
this->value = other.value;
return *this;
}
};
// Define operator+ for C2 out-of-line
template <class T>
inline C2<T> operator+(const C2<T>& self, const C2<T>& other) {
return self.value + other.value;
}
} // namespace N1
namespace {
using C1 = N1::C1<int>;
using C2 = N1::C2<int>;
// double f1(double x, const C1& y) {
// return (x + y).value; // not expected to work
// }
double f2(const C1& x, double y) {
return (x + y).value; // works
}
double f3(double x, const C2& y) {
// Works when C2 is a class, fails when C2 is a template class
return (x + y).value;
}
double f4(const C2& x, double y) {
// Works when C2 is a class, fails when C2 is a template class
return (x + y).value;
}
} // namespace
my hope is that clients should be able to write code such as
void my_main() {
N1::C2<Anything> x{4};
auto y = x + 2.0;
auto z = 2.0 + x;
}
You can fix C1's first test case by adding the out of line operator+
template<class T>
C1<T> operator+(double d, const C1<T>& c1) {
return c1 + d;
}
to get both test cases to pass:
double f1(double x, const C1& y) {
return (x + y).value; // not expected to work - but now works
}
double f2(const C1& x, double y) {
return (x + y).value; // works
}
The C2 tests: To fix those, you need to make N1::C2<int> a dependent type. You can do that with a bit of SFINAE which considers all N1::C2<T>s, but only accepts it when T is int. Note that it's a problem with the test cases - not with implicit conversion.
template<class T>
std::enable_if_t<std::is_same_v<T,int>, double>
f3(double x, const N1::C2<T>& y) {
return (x + y).value; // implicit conversion works
}
template<class T>
std::enable_if_t<std::is_same_v<T,int>, double>
f4(const N1::C2<T>& x, double y) {
return (x + y).value; // implicit conversion works
}
or using your C2 typedef which makes it consider all T's but only accepts N1::C2<int>:
using C2 = N1::C2<int>;
template<class T>
std::enable_if_t<std::is_same_v<T,C2>, double>
f3(double x, const T& y) {
return (x + y).value; // implicit conversion works
}
template<class T>
std::enable_if_t<std::is_same_v<T,C2>, double>
f4(const T& x, double y) {
return (x + y).value; // implicit conversion works
}
In the comments you say you want the functions to accept all N1::C2<T>s and then it becomes simpler:
template<class T>
auto f3(double x, const N1::C2<T>& y) {
return (x + y).value;
}
template<class T>
auto f4(const N1::C2<T>& x, double y) {
return (x + y).value;
}
My hope is that clients can write code such as ... Is that not possible?
N1::C2<int> x{4};
auto y = x + 2.0;
auto z = 2.0 + x;
Yes, but you then need to add overloads for that:
template <class T>
C2<T> operator+(const C2<T>& lhs, double rhs) {
return lhs.value + rhs;
}
template <class T>
C2<T> operator+(double lhs, const C2<T>& rhs) {
return rhs + lhs; // just swapped the order to use the above
}
Another option, which is how it's commonly done, is to add the operator+= member function:
template<class T>
class C2 {
// ...
C2& operator+=(const C2& other) {
value += other.value;
return *this;
}
};
You could then define the free functions like so:
template <class T>
C2<T> operator+(const C2<T>& lhs, std::convertible_to<C2<T>> auto&& rhs) {
auto rv = lhs;
rv += rhs;
return rv;
}
template <class T, class U>
std::enable_if_t<!std::same_as<std::decay_t<U>*, C2<T>*>, C2<T>>
operator+(const U& lhs, const C2<T>& rhs) {
return rhs + lhs;
}
Demo
Instead of SFINAE as above, you could create a home made concept to avoid ambiguity when adding two C2<T>s:
template <class From, class To>
concept convertible_to_but_is_not =
not std::same_as<std::remove_reference_t<From>, To> &&
std::convertible_to<From, To>;
template <class T>
C2<T> operator+(const C2<T>& lhs, std::convertible_to<C2<T>> auto&& rhs) {
auto rv = lhs;
rv += rhs;
return rv;
}
template <class T> // making sure that lhs is not a C2<T>
C2<T> operator+(convertible_to_but_is_not<C2<T>> auto&& lhs, const C2<T>& rhs) {
return rhs + lhs;
}
Demo
Per Ted's answer:
Implicit conversions are not considered from function arguments during template argument deduction. This problem can be addressed by the addition of two helper functions:
template <class T>
inline C2<T> operator+(double self, const C2<T>& other) {
return self + other.value;
}
template <class T>
inline C2<T> operator+(const C2<T>& self, double other) {
return self.value + other;
}

How to overload the assignment operator for a class that inherits from a template in c++

I have a given utility template class tagged. I had to declare 2 new structs using these template classes as follows.
tagged.h
#ifndef TAGGED_H
#define TAGGED_H
#include <iostream>
template<typename T, typename TAG>
class tagged
{
private:
T _value;
public:
tagged() : _value() { }
explicit tagged(const T& value) : _value(value) { }
// https://isocpp.org/wiki/faq/templates#template-friends
friend T& value(tagged<T, TAG>& st)
{
return st._value;
}
friend const T& value(const tagged<T, TAG>& st)
{
return st._value;
}
};
template<typename T>
struct equality
{
friend bool operator ==(const T& x, const T& y)
{
return value(x) == value(y);
}
friend bool operator !=(const T& x, const T& y)
{
return value(x) != value(y);
}
};
template<typename T>
struct ordered : equality<T>
{
friend bool operator <(const T& x, const T& y)
{
return value(x) < value(y);
}
friend bool operator <=(const T& x, const T& y)
{
return value(x) <= value(y);
}
friend bool operator >(const T& x, const T& y)
{
return value(x) > value(y);
}
friend bool operator >=(const T& x, const T& y)
{
return value(x) >= value(y);
}
};
These are the two structs i declared following the rules given by the assignment.
primitives.h
//Time
struct __declspec(empty_bases)Time : tagged<uint64_t, Time>, ordered<Time>, show_value<Time, int>{ using tagged::tagged; };
//Duration
struct __declspec(empty_bases)Duration : tagged<uint64_t, Duration>, ordered<Duration>, show_value<Duration, int> { using tagged::tagged; };
I succeeded in writing all other operators like + and - but i cant seem to solve how to overload += and -= I'm not allowed to change the objects in tagged.h I know assignment operators can only be member functions. Because of the way the template works i've tried casting 'const Time&' and const Duration& to non consts but that didnt seem to work. I've tried the examples you can find online about assigment operator overloading but the examples all overload in the template and not in the inherited class where I barely have write access to '_value' which is the value I should overwrite of reassign the pointer of.
Thanks
edit:
struct __declspec(empty_bases)Time : tagged<uint64_t, Time>, ordered<Time>, show_value<Time, int>
{
using tagged::tagged;
Time& operator+(const Duration& right) {
Time t = Time(value(*this) + value(right));
return t;
};
Time& operator+=(const Duration& right) {
(uint64_t&)(*this) = value(*this) + value(right);
return (*this);
};
};
//Duration
struct __declspec(empty_bases)Duration : tagged<uint64_t, Duration>, ordered<Duration>, show_value<Duration, int> {
using tagged::tagged;
Duration& operator+(const Duration& right) {
Duration d = Duration(value(*this) + value(right));
return d;
};
Time& operator+(const Time & right) {
Time t = Time(value(*this) + value(right));
return t;
};
Duration& operator-(const Time & right) {
Duration d = Duration(value(*this) - value(right));
return d;
};
Duration& operator-(const Duration & right) {
Duration d = Duration(value(*this) - value(right));
return d;
};
Duration& operator+=(const Duration& right) {
(uint64_t&)(*this) = (uint64_t&)(*this) + (uint64_t&)(right);
return (*this);
}
Duration& operator-=(const Duration& right) {
(uint64_t&)(*this) = value(*this) - value(right);
return (*this);
};
};
This is what I have now. Still have the same syntax errors that keep popping up. I dont know anymore lmao
From what I see you should be able to implement it using value() function; Since value returns by reference something like this should work:
Duration & operator+=(const Duration & right) {
value(*this) += value( right );
return *this;
}
Just be careful on the other operators (I'm looking at + and -) because you return references to temporal objects.
Duration & operator+(const Duration & right) { // this returns a reference to an object
Duration d = Duration( value( *this ) + value( right ) ); // this creates a temporal variable
return d; // you return a reference to d bu its lifetime is over -> undefined behavior I believe
}

Unified implementation of c++ '+' operator using '+=' operator

For example, I have 2 structures: a 2D vector and a 3D vector with defined += operators:
struct Vector2
{
double x;
double y;
....
Vector2& operator+=(const Vector2& vector);
....
};
struct Vector3
{
double x;
double y;
double z;
....
Vector3& operator+=(const Vector3& vector);
....
};
It is trivial to implement '+' operators for these structures using the corresponding '+=' operators:
Vector2 operator+(const Vector2& vector1, const Vector2& vector2)
{
Vector2 result(vector1);
result += vector2;
return result;
}
Vector3 operator+(const Vector3& vector1, const Vector3& vector2)
{
Vector3 result(vector1);
result += vector2;
return result;
}
I'd like to unify these 2 functions and replace them with one template function:
template <class T>
T operator+(const T& vector1, const T& vector2)
{
T result(vector1);
result += vector2;
return result;
}
But this function is so general that it makes operator+ ambiguous for other classes.
I tried to make this template applicable only for Vector2 and Vector3 structs using custom type traits and static_assert:
template <class T>
T operator+(const T& vector1, const T& vector2)
{
static_assert(suitable_type_for_this_function<T>::value, "Unsupported type!");
...
}
But it does not hide declaration of the template operator for other classes. Therefore this approach again leads to ambiguity.
How can I implement such a unified operator+, but define it only for these 2 specific types?
template<class D>
struct add_using_increment {
using Self=add_using_increment<D>;
friend D operator+(Self&& lhs, Self const& rhs){
lhs+=rhs;
return std::move(lhs.self());
}
friend D operator+(Self const& lhs, Self const& rhs){
return D(lhs.self())+rhs;
}
private:
D&self(){ return *static_cast<D*>(this); }
D const&self()const{ return *static_cast<D const*>(this); }
};
Now just do this:
struct Vector2:add_using_increment<Vector2>
Altenratively you can use SFINAE to restrict your template argument to (A) being one of a fixed set, (B) having a void increment( lhs&, rhs const& ) function defined on it, (C) having a += defined on it.
(C) is too greedy.
You can also use boost.operators which is an industrial strength version of my add_using_increment.
You can apply SFINAE, use std::enable_if with std::is_same to constrain the types allowed on T.
template <class T>
std::enable_if_t<std::is_same_v<T, Vector2> || std::is_same_v<T, Vector3>, T>
operator+(const T& vector1, const T& vector2);

Implicit conversion of int to double in template operator*<>

I have a template class typically instantiated by <double>.
My header has something like:
template <typename T> class F;
// Non-member functions
template <typename T> const F<T> operator*(F<T> lhs, const F<T>& rhs);
template <typename T> const F<T> operator*(const F<T>& lhs, const T& rhs);
template <typename T>
class F
{
// blah blah
F<T>& operator*=(const F<T>& rhs);
F<T>& operator*=(const T& rhs_scalar);
// blah
friend const F<T> operator*(const F<T>& lhs, const T& rhs) { return F(lhs) *= rhs; }
friend const F<T> operator*(const T& lhs, const F<T>& rhs) { return F(rhs) *= lhs; }
};
In my .cpp file, I have something like:
#include "F.H"
// lots of template<typename T> returntype F<T>::function(args){ body }
template <typename T>
F<T>& F<T>::operator*=(const F<T>& rhs)
{
// check sizes, etc
// do multiplication
return *this;
}
template <typename T>
F<T>& F<T>::operator*=(const T& rhs_scalar)
{
for(auto &lhs : field_) // field_ is a vector holding values
{
lhs *= rhs_scalar;
}
return *this;
}
// Non-member parts
template <typename T> operator*(F<T> lhs, const F<T>& rhs)
{ lhs *= rhs; return lhs; }
template <typename T> operator*(const F<T>& lhs, const T& rhs)
{ return F<T>(lhs) *= rhs; }
template class F<double>;
template const F<double> operator*<double>(F<double>, const F<double>&);
This compiles and runs ok, and allows things like:
F<double> test(..);
test *= 2.5; test *= 10; test /= 2.5; test /= 10; // and so on
The question I have is: Can I reduce the number of declarations and definitions of my operators, whilst retaining the ability to implicitly promote an int to a double, etc? Can I rearrange the code so that the friend .. operator*(..) bodies are defined outside of the header file? (I suspect this would involve more specific instantiations in one or both of the header and the cpp file)
(Side note: how? Scott Meyer's Item 46 in 'Effective C++' describes implicit parameter conversion, but it seems like that describes allowing the construction of a temporary object to allow (in that case) Rational(int) * Rational_object_already_created;)
Can I reduce the number of declarations and definitions of my operators, whilst retaining the ability to implicitly promote an int to a double, etc?
You can do that by providing a converting constructor.
template <typename T2>
F(F<T2> const& f2 ) {...}

operator overloading as a friend function

I create a template class and want to overload an operator + (several times). I do this in the following way
template <typename T> class Polynomial;
template <typename T>
const Polynomial<T> operator +(const Polynomial<T>& poly, const T& scalar);
....
template <typename T>
class Polynomial {
....
public:
....
Polynomial operator +(const Polynomial& other) const {
// impelementation
}
friend const Polynomial<T> operator + <> (const Polynomial<T>& poly, const T& scalar);
};
template <typename T>
const Polynomial<T> operator +(const Polynomial<T>& poly, const T& scalar) {
// implementation
}
However, I got the following error (which corresponds to the line that begins with 'friend')
problem2.cpp:282:45: error: declaration of ‘operator+’ as non-function
problem2.cpp:282:45: error: expected ‘;’ at end of member declaration
problem2.cpp:282:47: error: expected unqualified-id before ‘<’ token
Following Raxvan's advise, I've changed the code
template class Polynomial;
template <typename T>
const Polynomial<T> operator +(const Polynomial<T>& poly, const T& scalar);
template <typename T>
ostream& operator <<(ostream& out, const Polynomial<T>& other);
....
template <typename T>
class Polynomial {
....
public:
....
friend ostream& operator << <> (ostream& out, const Polynomial<T>& other);
Polynomial operator +(const Polynomial& other) const {
// impelementation
}
template <typename NOT_T>
friend const Polynomial<NOT_T> operator +(const Polynomial<NOT_T>& poly, const NOT_T& scalar);
};
template <typename T>
ostream& operator <<(ostream& out, const Polynomial<T>& other) {
// implementation
}
template <typename T>
const Polynomial<T> operator +(const Polynomial<T>& poly, const T& scalar) {
// implementation
}
And in this code I don't have problems with operator << as I had with operator +. Can anyone explain the defference?
The problem is subtle. Your syntax is very close to being
correct. I think your friend declaration should be:
friend Polynominal<T> const operator+<T>( ... );
but both VC++ and g++ accept:
friend Polynominal<> const operator+<T>( ... );
when they can see the declaration:
template <typename T>
const Polynomial<T> operator+(const Polynomial<T>& poly, const T& scalar);
(I can't find anything in the standard to justify this off hand,
but since both VC++ and g++ do it, I suppose that it's something
I've missed.)
And therein lies the problems. The declaration at namespace
scope of operator+ is hidden by the in-class definition. The
compiler complains because the operator+ it finds (the in
class definition) isn't a template itself (although it is
a member of a class template).
If you don't have the problem with operator<<, it's because
you don't have a member function with the same name.
There are several ways of solving this problem. The simplest is
probably to make all of the operator+ friends. Or not: the
most common approach to this is to implement operator+ in
terms of operator+= (which should be a member, in all cases).
In which case, operator+ doesn't have to be a friend.
Or you don't make operator+ a template at all, but definite it
inside your class template:
template <typename T>
class Polynomial
{
friend Polynomial operator+( Polynomial const& lhs, Polynomial const& rhs )
{
// ...
}
friend Polynomial operator+( Polynomial const& lhs, T const& rhs )
{
// ...
}
friend Polynomial operator+( T const& lhs, Polynomial const& rhs )
{
// ...
}
}
(Note that the functions being defined are not templates, but
separate overloaded non-template functions, one for each
instantiation of Polynomial. But the results end up being the
same.)
In this case, you probably would want to have a member function,
called by the operator+ functions, which would do the actual
work; you don't want too much code directly inline like this.
Your operator+ is a function template, to make this a friend, you need to declare it fully including the template parameters, however with a different template parameter, for example:
#include <iostream>
template <typename T> class Polynomial;
template <typename T>
const Polynomial<T> operator +(const Polynomial<T>& poly, const T& scalar);
template <typename T>
class Polynomial {
public:
Polynomial operator +(const Polynomial& other) const {
std::cout << "inline +" << std::endl;
}
// Here introduce a different type to indicate that this is a template...
template <typename U>
friend const Polynomial<U> operator + (const Polynomial<U>& poly, const U& scalar);
};
template <typename T>
const Polynomial<T> operator +(const Polynomial<T>& poly, const T& scalar) {
std::cout << "friend +" << std::endl;
}
int main(void)
{
Polynomial<int> f;
f = f + 1;
f = f + 1.; // this fails
f = f + Polynomial<int>();
}
You have to have the same definition when you label a function as friend, this includes the template used above with another type , not T
template <typename T> class Polynomial;
template <typename T>
const Polynomial<T> operator +(const Polynomial<T>& poly, const T& scalar);
template <typename T>
class Polynomial {
public:
template < class NOT_T> //must respect the deffinition from above
friend const Polynomial<NOT_T> operator + (const Polynomial<NOT_T>& poly, const NOT_T& scalar);
};
template <typename T>
const Polynomial<T> operator +(const Polynomial<T>& poly, const T& scalar)
{
//...
}
Edit:
For a simplified explanation i have changed the function to foo and bar to illustrate the difference in declarations:
template <typename T> class Polynomial;
template <typename T>
void bar(const Polynomial<T>& );
void foo(const Polynomial<float> & );//foo for speciffic float
template <typename T>
class Polynomial {
public:
template <typename> //this is also valid declaration;
friend void bar(const Polynomial<T> & );
//it just has to have template because it's a template functions declaration
//not a valid declaration:
//friend void bar <> (const Polynomial<T> & );
//this declaration has no template;
//it refers to a foo function specific to Polynomial<T> type
//so if you use Polynomial<float> there must be a foo for floats
friend void foo(const Polynomial<T> &);
};
template <class T>
void bar(const Polynomial<T>&)
{
}
void foo(const Polynomial<float> &)
{
}
void main()
{
Polynomial<float> pf;
Polynomial<int> pi;
foo(pi);//error since there is not **foo** declared for int
foo(pf);//ok; we have **foo** for Polynomial<float>
bar(pf);
bar(pi);//both ok since bar is templated function;
}
Raxvan.