misunderstanding the global operator overload rule - c++

I found the URL below that says that
If an operator can be used as either a unary or a binary
operator (&, *, +, and -), you can overload each use separately.
I am working with g++ in Linux and I tried the following and it didn't compile.
int operator+ (const int a,const int b){
std::cout << "MINE"<<std::endl;
return 0;
}
int main(){
char c='c';
std::cout << c+2 << std::endl;
}
The error says
error: ‘int operator+(int, int)’ must have an argument
of class or enumerated type
I was willing to play and see in action the Integer Promotion Rules.
Am I doing something wrong or that URL is valid only for MS or I misunderstood the promotion rule?

The error message indirectly tells you what you need to know -- you are not permitted to overload operators (binary or unary) that act only on built-in types.
For a user-defined type T, you can separately overload binary + (for example by T operator+(T lhs, T rhs)) and unary + (for example by T operator+(T t)). You could also define operator+(T lhs, int rhs), but you can't overload addition of two integers.

Example is incorrect since
n3337 13.5/6
An operator function shall either be a non-static member function or be a non-member function and have
at least one parameter whose type is a class, a reference to a class, an enumeration, or a reference to an
enumeration.

Related

templated operator "+" overloading and enum initialization [constexpr][msvc]

Please, consider the following code:
template<typename T>
T operator+(const T& lvh, const T& rvh)
{
return lvh;
}
struct enum_container {
enum {
item1 = 1,
item2 = 2,
item3 = item1 + item2
};
};
int main() {
return 0;
}
Compiling with MS VS 2022 gives the following error:
error C2131: expression did not evaluate to a constant
message : failure was caused by call of undefined function or one not declared 'constexpr'
message : see usage of 'operator +'
The problem applies to the third element in enum.
Why cl tries to substitute template for the constant expression?
Compilation with gcc gives no errors.
I can eliminate the error with cl adding type specifier to the enum, like this:
enum : int {
...
}
But I can`t do this in the project I work with. Both enum and overload declared by 3rd party headers. All I can do is to change includ order. But I'm still wondering why it works that way...
To me it looks like MSVC is correct here.
First, because template argument deduction should happen before overload resolution to form a set of candidate functions:
Before overload resolution begins, the functions selected by name
lookup and template argument deduction are combined to form the set of
candidate functions
And then, since the non-member candidates (in this case template specialisation of the operator+ overload with the enum_container::<anonymous_enum> parameters) precede built-in candidates (conversion to the underlying built-in type, for which a user-defined overload would not be possible), the compiler is meant to resolve the overload to the instance of the template with the user-defined enum-type:
non-member candidates: For the operators where operator overloading
permits non-member forms, all declarations found by unqualified name
lookup of operator# in the context of the expression (which may
involve ADL), except that member function declarations are ignored and
do not prevent the lookup from continuing into the next enclosing
scope. If both operands of a binary operator or the only operand of a
unary operator has enumeration type, the only functions from the
lookup set that become non-member candidates are the ones whose
parameter has that enumeration type (or reference to that enumeration
type)
built-in candidates: For operator,, the unary operator&, and the operator->, the set of built-in candidates is empty. For other
operators built-in candidates are the ones listed in built-in operator
pages as long as all operands can be implicitly converted to their
parameters. If any built-in candidate has the same parameter list as a
non-member candidate that isn't a function template specialization, it
is not added to the list of built-in candidates. When the built-in
assignment operators are considered, the conversions from their
left-hand arguments are restricted: user-defined conversions are not
considered.
Thus, the fact that GCC/Clang ignore the existence of the user-defined overload of operator+ under the enum definition doesn't seem to be compliant with the rules listed above. The same expression inside of a block scope causes exactly same error for all compilers (and I couldn't find any rationale why this might be different for the scope of enum definition):
struct enum_container {
enum {
item1 = 1,
item2 = 2,
item3 = item1 + item2
};
};
template<typename T>
T operator+(const T& lhs, const T&) {
return lhs;
}
int main() {
// error: call to non-'constexpr' function 'T operator+(const T&, const T&)
constexpr auto enumval = enum_container::item1 + enum_container::item2;
return 0;
}

What's the difference between non-member function and member function when type conversions should apply to all parameters?

What's the difference between non-member function and member function when type conversions should apply to all parameters?
That from the book, "Effective C++":
First, make operator* a member function:
class Rantional {
public:
Rantional(int numerator=0,int denominator=0)
: numerator(numerator), denominator(denominator){
std::cout << "constrctor be called" << std::endl;
}
const Rantional operator*(const Rantional& rhs){
return Rantional(numerator * rhs.numerator, denominator * rhs.denominator); // member function
}
private:
int numerator;
int denominator;
};
Rantional oneHalf(1,2);
Rantional result = 2 * oneHalf; // error
Second, make operator* a non-member function:
const Rantional operator*(const Rantional& lhs,const Rantional& rhs){
return Rantional(lhs.numerator * rhs.numerator, lhs.denominator * rhs.denominator); // non-member function
Rantional oneHalf(1,2);
Rantional result = 2 * oneHalf; // ok
What's different above?
There is a explanation of it in the book, but I can't understand. So can you tell me with a simple description?
The implicit parameter corresponding to the object on which the member function is invoked — the one this points to — is never eligible for implicit conversions.
Consider the following statement from both examples:
Rational result = 2 * oneHalf;
To make this compile using an operator declared as a method on the class, the integer literal '2' would need to be converted (implicitly) to a 'Rational' type. This conversion is not considered by the compiler. The member operator* is never added to the list of function candidates during overload resolution and as a result is never found. Note that the statement:
Rational result = oneHalf * 2;
Will work fine, no implicit conversion on the left hand side is necessary. Since 'oneHalf' is already a 'Rational' the member operator* will be added to the set of candidates during overload resolution and will be found and used.
The non-member version of the operator* function will be considered during resolution because it is in global or namespace scope and will form a match with a single implicit conversion on the 'int' literal.
A lot of detailed information about how overload resolution is performed can be found on: https://en.cppreference.com/w/cpp/language/overload_resolution

What type does the conversion logic target?

I don't understand why in the following code the expression C c3 = 5 + c;
doesn't get compiled although 5 could be converted to type C as in the previous statement.
#include <iostream>
class C
{
int m_value;
public:
C(int value): m_value(value) {} ;
int get_value() { return m_value; } ;
C operator+(C rhs) { return C(rhs.m_value+m_value); }
};
int main()
{
C c = 10;
C c2 = c + 5; // Works fine. 5 is converted to type C and the operator + is called
C c3 = 5 + c; // Not working: compiler error. Question: Why is 5 not converted to type C??
std::cout << c.get_value() << std::endl; // output 10
std::cout << c2.get_value() << std::endl; // output 15
}
Because if overload operator as member function of the class, it could only be called when the object of that class is used as left operand. (And the left operand becomes the implicit *this object for the member function to be called.)
Binary operators are typically implemented as non-members to maintain symmetry (for example, when adding a complex number and an integer, if operator+ is a member function of the complex type, then only complex+integer would compile, and not integer+complex).
From the standard, [over.match.oper]/3
(emphasis mine)
For a unary operator # with an operand of a type whose cv-unqualified
version is T1, and for a binary operator # with a left operand of a
type whose cv-unqualified version is T1 and a right operand of a type
whose cv-unqualified version is T2, four sets of candidate functions,
designated member candidates, non-member candidates, built-in
candidates, and rewritten candidates, are constructed as follows:
(3.1) If T1 is a complete class type or a class currently being defined, the set of member candidates is the result of the qualified
lookup of T1::operator# ([over.call.func]); otherwise, the set of
member candidates is empty.
That means if the type of left operand is not a class type, the set of member candidates is empty; the overloaded operator (as member function) won't be considered.
You can overload it as a non-member function to allow the implicit conversion for both left and right operands.
C operator+(C lhs, C rhs) { return C(lhs.get_value() + rhs.get_value()); }
then both c + 5 or 5 + c would work fine.
LIVE
BTW: This will cause one temporaray object being constructed (from int to C) for the non-member function to be called; if you care about that, you can add all the three possible overloads as follows. Also note that this is a trade-off issue.
C operator+(C lhs, C rhs) { return C(lhs.get_value() + rhs.get_value()); }
C operator+(C lhs, int rhs) { return C(lhs.get_value() + rhs); }
C operator+(int lhs, C rhs) { return C(lhs + rhs.get_value()); }
And here're some suggestions about when to use a normal, friend, or member function overload.
In most cases, the language leaves it up to you to determine whether
you want to use the normal/friend or member function version of the
overload. However, one of the two is usually a better choice than the
other.
When dealing with binary operators that don’t modify the left operand
(e.g. operator+), the normal or friend function version is typically
preferred, because it works for all parameter types (even when the
left operand isn’t a class object, or is a class that is not
modifiable). The normal or friend function version has the added
benefit of “symmetry”, as all operands become explicit parameters
(instead of the left operand becoming *this and the right operand
becoming an explicit parameter).
When dealing with binary operators that do modify the left operand
(e.g. operator+=), the member function version is typically preferred.
In these cases, the leftmost operand will always be a class type, and
having the object being modified become the one pointed to by *this is
natural. Because the rightmost operand becomes an explicit parameter,
there’s no confusion over who is getting modified and who is getting
evaluated.
You are facing the reason to define certain operator overloads as free functions, i.e., when implicit conversions are desired. To see what's going on under the hood, consider the verbose form of operator overload invocations:
C c2 = c.operator+(5); // Ok, c has this member function
C c3 = 5.operator+(c); // No way, this is an integer without members
You can obviously do is an explicit C construction as in
C c3 = C{5} + c;
but this is not intended for an arithmetic value type like C. To make the implicit construction possible, define the overload as a free function
auto operator + (C lhs, const C& rhs)
{
lhs += rhs;
return lhs;
}
Now, there is no restriction of the left hand side operand. Note that the operator is implemented in terms of += (you would have to implement it to make the above code compile), which is good practice as pointed out in this thread: when you provide a binary operator + for a custom type, users of that type will also expected operator += to be available. Hence, to reduce code duplication, it's usually good to implement + in terms of += (same for all other arithmetic operands).
Further note that these operands often require a substantial amount of boilerplate code. To reduce this, consider e.g. the Boost operators library. To generate all standard arithmetic operators based on the minimal amount of actual hand-written code:
#include <boost/operators.hpp>
class C : private boost::arithmetic<C>
// ^^^^^^^^^^^^^^^^^^^^
// where the magic happens (Barton-Nackmann trick)
{
int m_value ;
public:
C(int value): m_value(value) {} ;
C& operator+=(const C& rhs) { m_value += rhs.m_value; return *this; }
C& operator-=(const C& rhs) { m_value -= rhs.m_value; return *this; }
C& operator*=(const C& rhs) { m_value *= rhs.m_value; return *this; }
C& operator/=(const C& rhs) { m_value /= rhs.m_value; return *this; }
const C& operator+() const { return *this; }
C operator-() const { return {-m_value}; }
int get_value() { return m_value; } ;
};
Here's an additional remark (a bit of a "reductio ad absurdum") on why your suggestion that the compiler could implicitly convert the left hand argument to a C would, essentially, open a can of worms. The actual language rules say, simply put, that, before applying conversions, a name lookup – for function calls and calls to (user-declared) operators – is done to find a candidate set. At this point, the operand types are not yet considered, but the scope very well is. So the type of the first argument does matter insofar as a user-declared operator is only in scope if its first argument is of the (cv-qualified) class type it is declared in. When a candidate set has been found, the compiler then tries to apply the conversion rules and ranks the candidates etc.
(Your question is therefore a bit misleading because in your example, we don't even get to the conversion logic, instead name resolution already comes up empty.)
Now, imagine we could simply change the language to say that the first argument can also be converted, prior to name resolution. A little bit of handwaving is required here, because this means we have to do conversions, look up names, and then do conversions again, so how this would work in practice is certainly unclear. Anyway, look at this example then:
struct B;
struct A
{
A(int);
A operator +(B) const;
};
struct B
{
B(int);
B operator +(B) const;
};
Now, what should 1 + B{3} do? Apparently, it could be transformed to B{1} + B{3}. But who's to say we couldn't do A{1} + B{3} instead? Why would B's constructor be preferred over A's? Of course, we could argue that either B is to be preferred, because, look at how nice and symmetric B{...}+B{...} is (ok, I'm being slightly facetious). Or we could take the safer route of saying that the program is ill-formed if it contains such an ambiguity. But there are a lot more corner cases to consider, e.g. what if B's constructor was made explicit – should the compiler (still or newly) error out, or should it silently switch to the usable implicit conversion to A?
Another non-obvious point is which types in which scopes (e.g. namespaces) should be considered? It would certainly be surprising if you use operator + in e.g. global namespace scope, and the compiler would dig out some type __gnucxx::__internal::__cogwheels::__do_something_impl, implcitly convert an operand to it, and then perform an operation on that.
Also note that this feature even if it can be specified in a reasonable and clean manner, could have quite a compile-time cost (in fact, overload resolution is already one of the biggests costs when compiling C++ and one of the reasons why compiling C++ code can take a lot longer than compiling C).
TL;DR:
There are tricky corner cases.
The benefit is marginal (why not make such operators free functions as others have pointed out)?
The discussions on how to standardize this would certainly be long.

overloading non-member conversion to bool operator

I am trying to write bool-conversion operator for std::bitset
I tried:
template<size_t size>
operator bool(std::bitset<size> & b)
{
return b.any();
}
but I got
error C2801: 'mynamespace::operator bool' must be a non-static member
from my visual-studio.
But when I look up C2801 explanation it says nothing about conversion operators (only about =, ->, [],())
So, is it possible to somehow write "Conversion std::bitset to bool operator?"
(I can not call b.any() in my if-statements, because the same code must run when std::bitset is replaced with unsigned or something
typedef std::bitset<x> Bitset;
//typedef unsigned Bitset;
so the ideal syntax will be like:
Bitset b = whatewer;
if(b)
doStuff();
)
If this overloading is not possible, what is the recommended workaround?
so far I use it like:
if(b == Bitset(0))
doStuff();
but I dont like it.
Thank you
As the error message says, the conversion operator must be a non-static member of a class. That is true.
I can not call b.any() in my if-statements, because the same code must run when std::bitset is replaced with unsigned or something.
If that is your problem, then you can use function overload, and call it passing the argument which will return a boolean value:
template<typename T>
bool to_bool(T const & b)
{
return b; //implicit conversion (if allowed) for all other types
}
template<size_t N>
bool to_bool(std::bitset<N> const & b)
{
return b.any();
}
then use it as:
if (to_bool(whatever))
{
}
It will call the correct overload. If the type of whatever is std::bitset<N> then the second overloaded function will be called, or else the first one will be called.
§12.3.2/1: "A member function of a class X with a name of the form [...] specifies a conversion from X to the type specified..." (C++11 uses the same section number and nearly the same wording, adding only that the function takes no parameters).
The other possible way to define a conversion is a constructor (§12.3.1), which is obviously a class member as well.
In short, yes, conversions must always be defined as member functions.
One way to do what you want would be to write a wrapper around std::bitset that provides the conversion you care about:
template <int size>
class mybitest {
std::bitset<size> bits;
public:
operator bool() { return bits.any(); }
}
But if you decide to do that, you'll need to write forwarding functions for essentially all the pieces of bitset you're using (ctors, assignment, etc.)
The standard is a bit unclear on this (12.3.2):
A member function of a class X having no parameters with a name of the form [...] specifies a conversion from X to the type specified by the conversion-type-id. Such functions are called conversion functions. No return type can be specified. If a conversion function is a member function, the type of the conversion function (8.3.5) is “function taking no parameter returning conversion-type-id”.
The first sentence seems to imply that only member functions can be conversion functions, but I'm not sure what the purpose of the conditional "if a conversion function is a member function" is.
I'd take the first sentence as binding and conclude that a conversion function must be a member function.
in case this helps somebody, you can actually provide a not operator instead
template<size_t size>
operator !(std::bitset<size> & b)
{
return !b.any();
}
and use it like so using the !! idiom:
if (!!whatever)
{
}
still not ideal, but a bit closer I think.

Why friend overloaded operator is preferred to conversion operator in this case

Hi I have a code like this, I think both the friend overloaded operator and conversion operator have the similar function. However, why does the friend overloaded operator is called in this case? What's the rules?
Thanks so much!
class A{
double i;
public:
A(int i):i(i) {}
operator double () const { cout<<"conversion operator"<<endl;return i;} // a conversion operator
friend bool operator>(int i, A a); // a friend funcion of operator >
};
bool operator>(int i, A a ){
cout<<"Friend"<<endl;
return i>a.i;
}
int main()
{
A aa(1);
if (0 > aa){
return 1;
}
}
No conversion is necessary for the overloaded operator> to be called. In order for the built-in operator> to be called, one conversion is necessary (the user-defined conversion operator. Overload resolution prefers options with fewer required conversions, so the overloaded operator> is used.
Note that if you were to change the definition of your overloaded operator> to be, for example:
friend bool operator>(double i, A a);
you would get a compilation error because both the overloaded operator> and the built-in operator> would require one conversion, and the compiler would not be able to resolve the ambiguity.
I am not claiming that my answer is supported by the standards, but lets think about it logically.
When you hit this line:
0 > aa
You have two options. Either you call the provided operator:
friend bool operator>(int i, A a);
Which is 100% compatible, or you can do two conversions to reach your destination! Which one would you choose?
If you add a conversion operator then an object of type A may be converted to double when you least expect it.
A good program does not provide a way for his classes to be accidently used and the conversion operator opens up the opertunity for the class to be used in a whole host of unintended situations (normally situations where you would expect a compile time error are not because of the auto type conversion).
As a result conversion operators (and single argument constructors) should be treateed with some care because of situations were the compiler may do conversion when you least expect it.
It is called because it is an exact match in the context of the expression 0 > aa. In fact, it is hard to figure out how you came up with the "why" question. By logic, one'd expect a "why" question if the friend weren't called in this case.
If you change the expression to 0.0 > aa, the call will beciome ambiguous, because neuther path will be better than the other.