Is it possible to define different = operators for different template arguments. Let's assume that I want to use different methods for converting arguments of different types:
template <class T,class U>
class cTest
{
private:
public:
T x;
U y;
//typical case
cTest<T,U>& operator =(const cTest<T,U> &that)
{
return *this;
}
//operator = based on the LHS type 1
cTest<uint16_t,U>& operator =(const cTest<int,U> &that)
{
cout<<"cTest<uint16_t,U>& operator =(const cTest<int,U> &that)"<<endl;
return cTest<uint16_t,U>();
}
//operator = based on the LHS type 2
cTest<uint8_t,U>& operator =(const cTest<int,U> &that)
{
cout<<"cTest<uint8_t,U>& operator =(const cTest<int,U> &that)"<<endl;
return cTest<uint16_t,U>();
}
};
You are trying to overload operators/functions by return type. This is not allowed by the C++ standard:
13.1/2: Certain function declarations cannot be overloaded: — Function declarations that differ only in the return type cannot be
overloaded.
Possible workarounds:
You could consider using a function instead on an operator, passing by reference a variable for storing the return value. In this case the overload would be possible. But it's less handy than the assignment operator, and I guess that's no what you were looking for.
A better approach would be to add a separate conversion operator between cTest<uint16_t,U> and cTest<uint8_t,U>.
I suggest that you have a look at Template Metaprogramming.
Template Metaprogramming is a generic programming technique that uses extremely early binding. The compiler acts as an interpreter or a "virtual computer" that emits the instructions that make up the final program. It can be used for static configuration, adaptive programs, optimization and much more.
You can basically let the compiler decide what template definition to use, depending on the respective values. An example for a quaternion multiplication would look like:
template <typename Quaternion>
typename std::enable_if_t<sds::is_quaternion<Quaternion>::value, Quaternion>
operator+(const Quaternion &a, const Quaternion &b)
{
return Quaternion
(
a.u() + b.u(),
a.v() + b.v()
);
}
Related
I know that there are questions with the same topic, but this one asks for something more specific.
I am creating a personal use multi-type templated container. The details are not important, but here are basic operators that I wanted to use with my container:
obj[n] which returns its own type so that it can be used to assign values as in obj[n]=value; (as an lvalue)
overloaded obj[n] which returns value retrieved stored in the "array" as in something=obj[n]; (as an rvalue)
The second overloaded operator is the one I want to talk about.
Using an extremely minimal example, here's the basic look of my class:
template <typename T1, typename T2>
class example
{
/*some private and public definitions*/
example<T1, T2>& operator[] (int n)
{
current = n;
return *this;
}
template <typename U>
U operator[] (int n)
{
/*some operations*/
return something;
}
};
The problem is, I want to parametrize the return value of the overloaded operator.
I read in one question here, on stackoverflow, that it is only possible to do that by calling the operator "explicitly explicitly".
My question is, how can I workaround this problem, so that if I can't parametrize an usual operator call, at least to implement my rvalue operator[] differently for what I want to achieve to be possible?
There are two operators that might be overloaded in your code. The [] operator and the = operator. You could overload [] to return an rvalue to a special class, and then overload = for that special class depending on whether that class appears on the left or right of the operator.
I have been trying to solve this bug for days. I made a generic array class, from which I used generic inheritance to create a numeric array. Everything works perfect; however, I am having trouble with multiplication/scaling of the numeric array. I overloaded the operator* in the following standard way:
//Scaling operator
template <typename Type>
NumericArray<Type>& NumericArray<Type> :: operator * (const double factor) // scaling the elements
{
for (int i = 0; i < (*this).size(); i++)
{
(*this)[i] *= factor;
}
return *this;
}
Of course, due to the inherent order demanded by this implementation, I can only do multiplication in the way of array * 2. But I also want to be able to write 2 * array. So I decided to implement a friend function as follows:
template <typename Type>
NumericArray<Type> operator* (Type factor, const NumericArray<Type>& num_array)
{
return(num_array * factor);
}
The declaration of this friend function is as follows:
template <typename Type> friend NumericArray<Type> operator * (double factor, const NumericArray<Type>& num_array);
But when I try to write 2 * array in main(), I get the error message:
Severity Code Description Project File Line
Error C2678 binary '*': no operator found which takes a left-hand operand of type 'const NumericArray' (or there is no acceptable conversion)
The error makes me think that main() doesn't even recognize that the friend function exists. I have used friend functions a few times before, but I am pretty new to templates. I've been told that templates and generic programming in general has some weird quirky necessities, which may cause unforeseen circumstances.
For anyone who wants to see the full program, see the dropbox link:
https://www.dropbox.com/sh/86c5o702vkjyrwx/AAB-Pnpl_jPR_GT4qiPYb8LTa?dl=0
Thanks again for your help:)
NumericArray<Type> in the friend function is const, it can not call a not const function--- NumericArray<Type>& NumericArray<Type> :: operator * (const double factor)
NumericArray<Type>& operator * (const double factor)
This is a non const member function. This means it may mutate the object it's called on.
Your parameter in the free operator function (which doesn't need to be a friend if calling the public operator above) is
const NumericArray<Type>&
and therefore const qualified.
To solve it, you need to change the member function to be callable on const qualified instances (given that it really does not mutate it's instance, which it shouldn't)(*):
NumericArray<Type>& operator * (const double factor) const
(*) Yours is mutating it's instance, this is counter intuitive:
c = a * b;
You don't want a to be changed after that, in general.
As Nicky C suggests in his answer, the best approach here is probably to have an operator*= as (non const, public) member function, which is doing what currently your operator* seems to be doing.
Then you add two free operator* functions which use the operator*= to implement their behaviour.
So no need for friends in this case.
I am not sure whether I want to explain everything since it can be complicated if we go deep. But in short:
You've mixed up the meanings and implementations of *= and *.
The errors involve a lot of things: template instantiation, const-correctness, overload resolution...
So, let's just look at the idiomatic way to do so.
Step 1: Write a member operator *=.
template <typename Type>
class NumericArray
{
//...
NumericArray& operator *= (const Type factor)
{
[[ Code that scales up every element, i.e. your for-loop ]]
return *this;
}
//...
}
Step 2: Write a pair of non-member non-friend operator *
template <typename Type>
NumericArray<Type> operator * (const NumericArray<Type>& num_array, const Type factor)
{
NumericArray<Type> new_num_array = num_array; // Copy construct
new_num_array *= factor; // Scale up
return new_num_array;
}
// And similarly for factor*num_array
Why it is not allowed to overload "=" using friend function?
I have written a small program but it is giving error.
class comp
{
int real;
int imaginary;
public:
comp(){real=0; imaginary=0;}
void show(){cout << "Real="<<real<<" Imaginary="<<imaginary<<endl;}
void set(int i,int j){real=i;imaginary=j;}
friend comp operator=(comp &op1,const comp &op2);
};
comp operator=(comp &op1,const comp &op2)
{
op1.imaginary=op2.imaginary;
op1.real=op2.real;
return op1;
}
int main()
{
comp a,b;
a.set(10,20);
b=a;
b.show();
return 0;
}
The compilation gives the following error :-
[root#dogmatix stackoverflow]# g++ prog4.cpp
prog4.cpp:11: error: ‘comp operator=(comp&, const comp&)’ must be a nonstatic member function
prog4.cpp:14: error: ‘comp operator=(comp&, const comp&)’ must be a nonstatic member function
prog4.cpp: In function ‘int main()’:
prog4.cpp:25: error: ambiguous overload for ‘operator=’ in ‘b = a’
prog4.cpp:4: note: candidates are: comp& comp::operator=(const comp&)
prog4.cpp:14: note: comp operator=(comp&, const comp&)
Because if you do not declare it as a class member compiler will make one up for you and it will introduce ambiguity.
The assignment operator is explicitly required to be a class member operator. That is a sufficient reason for the compiler to fail to compile your code. Assignment is one of the special member functions defined in the standard (like the copy constructor) that will be generated by the compiler if you do not provide your own.
Unlike other operations that can be understood as external to the left hand side operator, the assignment is an operation that is semantically bound to the left hand side: modify this instance to be equal to the right hand side instance (by some definition of equal), so it makes sense to have it as an operation of the class and not an external operation. On the other hand, other operators as addition are not bound to a particular instance: is a+b an operation of a or b or none of them? -- a and b are used in the operation, but the operation acts on the result value that is returned.
That approach is actually recommended and used: define operator+= (that applies to the instance) as a member function, and then implement operator+ as a free function that operates on the result:
struct example {
example& operator+=( const example& rhs );
};
example operator+( const example& lhs, const example& rhs ) {
example ret( lhs );
ret += rhs;
return ret;
}
// usually implemented as:
// example operator+( example lhs, const example& rhs ) {
// return lhs += rhs; // note that lhs is actually a copy, not the real lhs
//}
Assignment(=) operator is a special operator that will be provided by the constructor to the class when programmer has not provided(overloaded) as member of the class.(like copy constructor).
When programmer is overloading = operator using friend function, two = operations will exists:
1) compiler is providing = operator
2) programmer is providing(overloading) = operator by friend function.
Then simply ambiguity will be created and compiler will gives error. Its compilation error.
There is no good reason for that, I think Stepanov proposed that there should be a free operator= and many good stuff can be done with that (even more than what can be done with the move assignment). I can't find the citation but Stepanov went as a far as to suggest that the constructors could be free functions http://www.stlport.org/resources/StepanovUSA.html.
There is a way around it, which is to systematically declare a template<class Other> A& operator=(Other const& t); in inside all your classes, in this way you leave the option to anyone to define a custom assignment operator.
Of course you can't do this with a class you don't have control over.
Having said that it is nowadays not that bad since we have move assignment. And in some sense conversion operators B::operator A() const{...} are almost like a custom copy assignment. Conversion operators are now usable because of explicit. However you have to have control over the second type (B), the right-hand type in assignment.
Next question is why conversion operator need to be members them selves? Again, I don't think there is a good reason.
I wrote an abstraction class for a math object, and defined all of the operators. While using it, I came across:
Fixed f1 = 5.0f - f3;
I have only two subtraction operators defined:
inline const Fixed operator - () const;
inline const Fixed operator - (float f) const;
I get what is wrong here - addition is swappable (1 + 2 == 2 + 1) while subtraction is not (same goes for multiplication and division).
I immediately wrote a function outside my class like this:
static inline const Fixed operator - (float f, const Fixed &fp);
But then I realized this cannot be done, because to do that I would have to touch the class's privates, which results to using the keyword friend which I loath, as well as polluting the namespace with a 'static' unnecessary function.
Moving the function inside the class definition yields this error in gcc-4.3:
error: ‘static const Fixed Fixed::operator-(float, const Fixed&)’ must be either a non-static member function or a non-member function
Doing as GCC suggested, and making it a non-static function results the following error:
error: ‘const Fixed Fixed::operator-(float, const Fixed&)’ must take either zero or one argument
Why can't I define the same operator inside the class definition? if there's no way to do it, is there anyway else not using the friend keyword?
Same question goes for division, as it suffers from the same problem.
If you need reassuring that friend functions can be OK:
http://www.gotw.ca/gotw/084.htm
Which operations need access to
internal data we would otherwise have
to grant via friendship? These should
normally be members. (There are some
rare exceptions such as operations
needing conversions on their left-hand
arguments and some like operator<<()
whose signatures don't allow the *this
reference to be their first
parameters; even these can normally be
nonfriends implemented in terms of
(possibly virtual) members, but
sometimes doing that is merely an
exercise in contortionism and they're
best and naturally expressed as
friends.)
You are in the "operations needing conversions on the left-hand arguments" camp. If you don't want a friend, and assuming you have a non-explicit float constructor for Fixed, you can implement it as:
static inline Fixed operator-(const Fixed &lhs, const Fixed &rhs) {
return lhs.minus(rhs);
}
then implement minus as a public member function, that most users won't bother with because they prefer the operator.
I assume if you have operator-(float) then you have operator+(float), so if you don't have the conversion operator, you could go with:
static inline Fixed operator-(float lhs, const Fixed &rhs) {
return (-rhs) + lhs;
// return (-rhs) -(-lhs); if no operator+...
}
Or just Fixed(lhs) - rhs if you have an explicit float constructor. Those may or may not be as efficient as your friend implementation.
Unfortunately the language is not going to bend over backwards to accommodate those who happen to loathe one of its keywords, so operators can't be static member functions and get the effects of friendship that way ;-p
"That's what friends are for..."
You could add an implicit conversion between float and your type (e.g. with a constructor accepting float)... but I do think using a friend is better.
When you define something like this,
inline const Fixed operator - (float f) const;
you are saying that I want this operator(you are inside the class) to operate on a specific type, float here for example.
Whereas a friend binary operator, means an operation between two types.
class Fixed
{
inline friend const Fixed operator-(const Fixed& first, const float& second);
};
inline const Fixed operator-(const Fixed& first, const float& second)
{
// Your definition here.
}
with friend operators you can have your class on either side of the operator it self.
In general, free function operators for arithmetic operations are better than implementing member functions. The main reason is the problem you are facing now. The compiler will treat the left and right sides differently. Note that while strict OO followers will consider only those methods inside the class curly braces part of its interface, but it has been argued by experts that not to be the case in C++.
If the free function operator requires access to private members, make the operator friend. After all if it is provided in the same header file (following Sutter's rationale above) then it is part of the class.
If you really want to avoid it and don't mind making your code less idiomatic (and thus less maintainable) you can provide a public method that does the real work and dispatch to that method from the operator.
class Fixed {
private:
Fixed();
Fixed( double d ); // implicit conversion to Fixed from double
Fixed substract( Fixed const & rhs ) const;
// ...
};
Fixed operator-( Fixed const & lhs, Fixed const & rhs )
{
return lhs.substract( rhs );
}
In the code above, you can substract Fixed - Fixed, Fixed - double, double - Fixed. The compiler will find the free function and implicitly convert (in the example through the double constructor) the doubles into Fixed objects.
While this is unidiomatic for arithmetic operators, it is close to the idiomatic way of proving a polymorphic dump operator. So while not being the most natural solution it won't be the most surprising code around either
// idiomatic polymorphic dump operator
class Base {
public:
virtual std::ostream& dump( std::ostream & ) const;
};
std::ostream& operator<<( std::ostream& o, Base const & d )
{
return d.dump( o );
}
Error:
error C2678: binary '==' : no operator found which takes a left-hand operand of type 'const entry' (or there is no acceptable conversion)
The function:
template <class T, int maxSize>
int indexList<T, maxSize>::search(const T& target) const
{
for (int i = 0; i < maxSize; i++)
if (elements[i] == target) //ERROR???
return i; // target found at position i
// target not found
return -1;
}
indexList.h
indexList.cpp
Is this suppose to be an overloaded operator? Being a template class I am not sure I understand the error?
Solution-
The overload function in the class now declared const:
//Operators
bool entry::operator == (const entry& dE) const <--
{
return (name ==dE.name);
}
Start by reading the error text exactly as it is:
binary '==' : no operator found which takes a left-hand operand of type 'const entry'
It means it can't find any == operator that accepts an entry type as its left operand. This code isn't valid:
entry const e;
if (e == foo)
You've showed us the code for your list class, but that's not what the error is about. The error is about a lack of operators for the entry type, whatever that is. Either give the class an operator== function, or declare a standalone operator== function that accepts a const entry& as its first parameter.
struct entry {
bool operator==(const entry& other) const;
};
// or
bool operator==(const entry& lhs, const entry& rhs);
I think the latter is the preferred style.
The problem refers to the type T that is being used in this instance not having an operator== defined. I would guess from your question that this is a class 'entry'.
It might also be that the 'entry' class does not have the operator== defined correctly to take a const entry& as a parameter.
This is likely "const poisoning", where your use of const in the declaration of your search function will force you to add const versions of all the downstream functions you call.
In a function declared const, the this pointer is considered const, which means that all the members you use through that pointer are considered const as well. If your operator == () for whatever type T you are specializing with doesn't explicitly specify const parameters, you will get this error.
If you can't ensure that all Ts you use will have the proper operator == () calls, I'd remove the const specifiers on the member function templates.
The type T you're using as a parameter to this class should have an operator==() since the code you give doesn't contain the instantiation of the template its difficult to know what's wrong.
On a different note, the function definitions of a template should be in the .h file along with the class or else the compiler would not be able to instantiate it properly.
The equality operator for user-defined types is not defined by default. This doesn't have anything to do with your template class, but rather your struct or class "entry".
Therefore in you'll have to override the equality operator in struct entry or class entry.
Alternatively, if you don't want to force everything that uses that template to define an equality operator, you can modify the template interface to accept a Comparator that does the equality comparison.
Sometimes it is quite enough to write
...
if (target == elements[i])
...