In operator lookup no preference is given to members over nonmembers - c++

Stroustrup writes :
Consider a binary operator #. If x is of type X and y is of type Y, x#y is resolved like this:
• If X is a class, look for operator# as a member of X or as a member of a base of X; and
• look for declarations of operator# in the context surrounding x#y; and
• if X is defined in namespace N, look for declarations of operator# in N; and
• if Y is defined in namespace M, look for declarations of operator# in M.
Declarations for several operator#s may be found and overload resolution rules (§12.3) are used to find the best match, if any. This lookup mechanism is applied only if the operator has at least one operand of a user-defined type. Therefore, user-defined conversions (§18.3.2, §18.4) will be considered.
Note that a type alias is just a synonym and not a separate user-defined type (§6.5).
Unary operators are resolved analogously.
Note that in operator lookup no preference is given to members over nonmembers. This differs from lookup of named functions
So what means the bold expression. If class has member and in the same time there is non-member function which could be used in the context, then no preference is given to member? For example
class A
{
public:
bool operator==(const A&)
{
return true;
}
};
bool operator==(const A&, const A&)
{
return true;
}
int main()
{
A a, b;
a == b;
}
I think(and compilers agree with me :) ) that member function should be called, but as written above no preference should be given to member operator. So what exactly means Stroustrup by that sentence?

Your code only compiles because your operators are not equivalent in this context. One is better than the other, according to the rules of implicit conversion sequence ranking.
Note that your member operator lacks const-qualification for its first (left) argument. It actually stands for bool operator==(A&, const A&).
Since your a and b in main are not declared as const, the implicit conversion sequence for the left-hand operand is shorter (better) for member operator. It does not require a qualification conversion of a from A to const A. This is what makes it a better candidate. This is why your member function version is selected.
To make both versions equivalent you have to add const to your member operator declaration
class A
{
public:
bool operator==(const A&) const
{
return true;
}
};
and that will immediately make your a == b comparison ambiguous.

Related

What is the purpose of `operator auto() = delete` in C++?

A class in C++ can define one or several conversion operators. Some of them can be with auto-deduction of resulting type: operator auto. And all compilers allow the programmer to mark any operator as deleted, and operator auto as well. For concrete type the deletion means that an attempt to call such conversion will result in compilation error. But what could be the purpose of operator auto() = delete?
Consider an example:
struct A {
operator auto() = delete;
};
struct B : A {
operator auto() { return 1; }
};
int main() {
B b;
A a = b; // error in Clang
int i = b; // error in Clang and GCC
int j = a; // error in Clang and GCC and MSVC
}
Since the compiler is unable to deduce the resulting type, it actually prohibits any conversion from this class or from derived classes with the error:
function 'operator auto' with deduced return type cannot be used before it is defined.
Demo: https://gcc.godbolt.org/z/zz77M5zsx
Side note that compilers slightly diverge in what conversions are still allowed (e.g. GCC and MSVC permit the conversion to base class), which one of them is right here?
But what could be the purpose of operator auto() = delete?
What would be the purpose of the following function?
auto f() = delete;
As per the grammar functions, [dcl.fct.def.general]/1, the function-body of a function-definition may be = delete; e.g., it is syntactically valid to define a function as deleted.
C++14 introduced auto return type deduction for functions, and given the allowed grammar for function definitions, as per C++14 the grammar allows explicitly-deleting a function with auto return type.
Whether this corner case is useful or not is not really for the language ponder about, as there is always a cost of introducing corner cases (e.g. "the grammar for definitions of functions shall have a special case for auto type deduction"). Whilst there are limitations on where one may provide explicitly-defaulted function definitions ([dcl.fct.def.default]), the same restrictions do not apply for explicitly-deleted function definitions ([dcl.fct.def.delete]).
which one of them is right here?
A a = b; // error in Clang
Clang is arguable wrong to pick the user-defined conversion function for this initialization. As per [dcl.init.general]/15.6, /15.6.2:
Otherwise, if the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. [...]
takes precedence over /15.6.3:
Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversions that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in [over.match.copy], and the best one is chosen through overload resolution ([over.match]). [...]
As a user of the language, providing a deleted definition of a function with auto return type could be used to semantically mark that no one should provide any kind of overload of a given function name.
auto f() = delete; // never expose a valid overload for f()
// elsewhere:
int f() { return 42; }
// error: functions that differ only in
// their return type cannot be overloaded
For the special case of user-defined conversion operators, an explicitly-default auto return type user-defined conversion function can be used e.g. in a intended-for-composition base class, similar to Scott Meyers C++03 trick of making a class non-copyable (before C++11 introduced = delete).
struct NoUserDefinedConversionFunctionsAllowed {
operator auto() = delete;
};
struct S : NoUserDefinedConversionFunctionsAllowed {
operator int() { return 1; } // can never be used
};

C++ post-increment: objects vs primitive types

We cannot use pre-increment on rvalues:
int i = 0;
int j = ++i++; // Compile error: lvalue required
If we define a class:
class A
{
public:
A & operator++()
{
return *this;
}
A operator++(int)
{
A temp(*this);
return temp;
}
};
then we can compile:
A i;
A j = ++i++;
What is the different between A object and int data type that
j = ++i++;
compiles with A but not with int?
This happens because when overloaded operators are defined as member functions, they follow some semantics which are more related to calling a member function, not to the behavior of the built-in operator. Note that by default, if we declare a non-static member function like:
class X {
public:
void f();
X g();
};
then we can call it on both lvalue and rvalue class type expressions:
X().f(); // okay, the X object is prvalue
X x;
x.f(); // okay, the X object is lvalue
x.g().f(); // also okay, x.g() is prvalue
When overload resolution for an operator expression selects a member function, the expression is changed to be just a call to that member function, so it follows the same rules:
++A(); // okay, transformed to A().operator++(), called on prvalue
A a;
++a; // okay, transformed to a.operator++(), called on lvalue
++a++; // also technically okay, transformed to a.operator++(0).operator++(),
// a.operator++(0) is a prvalue.
This sort of non-equivalence between built-in operators and overloaded operators also happens with the left subexpression of assignment: the pointless statement std::string() = std::string(); is legal, but the statement int() = int(); is not legal.
But you noted in a comment "I want to design a class that prevents ++a++". There are at least two ways to do that.
First, you could use a non-member operator instead of a member. Most overloaded operators can be implemented as either a member or non-member, where the class type needs to be added as an additional first parameter type of the non-member function. For example, if a has class type, the expression ++a will attempt to find a function as if it were a.operator++() and also a function as if it were operator++(a); and the expression a++ will look for functions for the expressions a.operator++(0) or operator++(a, 0).
(This pattern of trying both ways does not apply to functions named operator=, operator(), operator[], or operator->, because they may only be defined as non-static member functions, never as non-members. Functions named operator new, operator new[], operator delete, or operator delete[], plus user-defined literal functions whose names start like operator "", follow entirely different sets of rules.)
And when the class argument matches a real function parameter, instead of the "implicit object parameter" of a non-static member function, the type of reference used in the parameter, if any, controls as usual whether an argument can be an lvalue, rvalue, or either.
class B {
public:
// Both increment operators are valid only on lvalues.
friend B& operator++(B& b) {
// Some internal increment logic.
return b;
}
friend B operator++(B& b, int) {
B temp(b);
++temp;
return temp;
}
};
void test_B() {
++B(); // Error: Tried operator++(B()), can't pass
// rvalue B() to B& parameter
B b;
++b; // Okay: Transformed to operator++(b), b is lvalue
++b++; // Error: Tried operator++(operator++(b,0)), but
// operator++(b,0) is prvalue and can't pass to B& parameter
}
Another way is to add ref-qualifiers to member functions, which were added to the language in the C++11 version as a specific way of controlling whether a member function's implicit object argument must be an lvalue or rvalue:
class C {
public:
C& operator++() & {
// Some internal increment logic.
return *this;
}
C operator++(int) & {
C temp(*this);
++temp;
return temp;
}
};
Notice the & between the parameter list and the start of the body. This restricts the function to only accept an lvalue of type C (or something that implicitly converts to a C& reference) as the implicit object argument, similarly to how a const in the same spot allows the implicit object argument to have type const C. If you wanted a function to require an lvalue but allow that lvalue to optionally be const, the const comes before the ref-qualifier: void f() const &;
void test_C() {
++C(); // Error: Tried C().operator++(), doesn't allow rvalue C()
// as implicit object parameter
C c;
++c; // Okay: Transformed to c.operator++(), c is lvalue
++c++; // Error: Tried c.operator++(0).operator++(), but
// c.operator++(0) is prvalue, not allowed as implicit object
// parameter of operator++().
}
To get operator= to act more like it does for a scalar type, we can't use a non-member function, because the language only allows member operator= declarations, but the ref-qualifier will similarly work. You're even allowed to use the = default; syntax to have the compiler generate the body, even though the function isn't declared in exactly the same way an implicitly-declared assignment function would have been.
class D {
public:
D() = default;
D(const D&) = default;
D(D&&) = default;
D& operator=(const D&) & = default;
D& operator=(D&&) & = default;
};
void test_D() {
D() = D(); // Error: implicit object argument (left-hand side) must
// be an lvalue
}
It … just is. There are a few constraints that apply only to primitive types and not class types (well, you've found the most obvious one!).
It is largely because operators for built-in types are one thing, whereas for classes they are just member functions in disguise and therefore a completely different beast.
Is this confusing? I don't know; maybe.
Is there a really compelling reason for it? I don't know; possibly not. There's a certain inertia with primitive types: why change something that was in C just because you're introducing classes? What is the benefit of permitting this? On the other hand, would it not be overly strict to ban it for classes, whose implementation of operator++ could do something that, as the language designer, you haven't thought of?

Template assignment operator doesn't replace the default assignment operator

In C++ Templates The Complete Guide in section 5.3 Member Templates it's written:
Note that a template assignment operator doesn't replace the default
assignment operator. For assignments of stacks of the same type, the
default assignment operator is still called.
Is this correct, because when I ran below code:
#include<iostream>
using namespace std;
template<typename T>
class Pair
{
public:
T pair1,pair2;
Pair(T i,T j):pair1(i),pair2(j){}
template<typename T1>Pair<T>& operator=(Pair<T1>&);
};
template<typename T>
template<typename T1>
Pair<T>& Pair<T>::operator=(Pair<T1>& temp)
{
this->pair1 =temp.pair1*10;//At this point
this->pair2=temp.pair2;
return *this;
}
int main()
{
Pair<int>P1(10,20);
Pair<int>P2(1,2);
P2=P1;
cout<<P2.pair1<<' '<<P2.pair2<<endl;
return 1;
}
I got answer 100 20.
It didn't give the default assignment answer.
Is that a typing mistake in C++ Templates the Complete Guide?
C++ Templates: The Complete Guide By David Vandevoorde, Nicolai M.
Josuttis
Publisher : Addison Wesley
Pub Date : November 12, 2002 Pages : 552
The copy assignment operator is indeed implicitly declared and considered by overload resolution.
A user-declared copy assignment operator X::operator= is a
non-static non-template member function of class X [..]. If the class definition does not explicitly
declare a copy assignment operator, one is declared implicitly. [..]
The implicitly-declared copy assignment operator for a class X will
have the form
X& X::operator=(const X&)
if
each direct base class B of X has a copy assignment operator whose parameter is of type const B&, const volatile B& or B, and
for all the non-static data members of X that are of a class type M (or array thereof), each such class type has a copy assignment
operator whose parameter is of type const M&, const volatile M& or
M.
Otherwise, [..]
As you can see the implicitly-declared copy assignment operator for Pair<int> has one parameter of type Pair<int> const& - note the const in particular! Overload resolution favours non-const references over const ones if both can be bound to the argument, [over.ics.rank]/3:
Two implicit conversion sequences of the same form are
indistinguishable conversion sequences unless one of the following
rules applies:
—
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence
S2 if
[..]
S1 and S2 are reference bindings (8.5.3), and the types to which the
references refer are the same type except for top-level cv-qualifiers,
and the type to which the reference initialized by S2 refers is more
cv-qualified than the type to which the reference initialized by S1
refers.
The specialization of the template lacks a const in the reference parameter, thus it's a better match and is selected.
The default assignment operator accepts the argument as a const reference: http://en.cppreference.com/w/cpp/language/as_operator.
You have defined a version without const, and your version is better in the context of overload resolution (no conversion required).
Try with the following change:
int main()
{
Pair<int>P1(10,20);
Pair<int>P2(1,2);
const Pair<int>& x = P1;
P2=x;
cout<<P2.pair1<<' '<<P2.pair2<<endl;
return 1;
}
to see the expected result.

Is it possible to define an implicit conversion operator to std::array?

I am trying to have a C++ class that can be implicitly converted to std::array. Conversion works, but it is not implicit.
#include <array>
class A {
private:
std::array<float, 7> data;
public:
operator std::array<float, 7>&() { return data; }
operator const std::array<float, 7>&() const { return data; }
};
int main() {
A a;
a[1] = 0.5f; // fails to compile
auto it = a.begin(); // fails to compile
A b;
static_cast<std::array<float, 7>>(b)[1] = 0.5f; //ok
auto it2 = static_cast<std::array<float, 7>>(b).begin(); //ok
return 0;
}
I understand the above example is quite convoluted, as it basically completely exposes a private member of the class. But this is an oversimplified example, I am just trying to tackle the problem of why implicit conversions to std::array does not work.
I have tried the above example with both clang-3.2 and gcc-4.8. Neither compiles.
Even more perplexing is that if I use implicit conversion to pointer, compilation apparently succeeds:
operator float *() { return data.begin(); }
operator const float *() const { return data.cbegin(); }
But of course, this means losing the many niceties of std::array, which I will accept if there isn't a better solution.
I'm answering your question from a comment:
Could you please elaborate on why my conversion does not make sense? While trying to resolve operator[], why should the compiler not consider possible conversions?
Short answer, because that's how it works. A conversion operator to a built-in type can be called here, not to user-defined type.
A bit longer answer:
When an operator is used in an expression, overload resolution follows the rules laid out in 13.3.1.2.
First:
2 If either operand has a type that is a class or an enumeration, a user-defined operator function might be
declared that implements this operator or a user-defined conversion can be necessary to convert the operand
to a type that is appropriate for a built-in operator. In this case, overload resolution is used to determine
which operator function or built-in operator is to be invoked to implement the operator [...].
a[1] is, for this purpose interpreted as a.operator[](1), as shown in Table 11 in the same section.
The lookup is then performed as follows:
3 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, three sets of candidate functions, designated member candidates, non-
member candidates and built-in candidates, are constructed as follows:
— If T1 is a complete class type, the set of member candidates is the result of the qualified lookup of
T1::operator# (13.3.1.1.1); otherwise, the set of member candidates is empty. [1]
— The set of non-member candidates is the result of the unqualified lookup of operator# in the context
of the expression according to the usual rules for name lookup in unqualified function calls (3.4.2)
except that all member functions are ignored. However, if no operand has a class type, only those
non-member functions in the lookup set that have a first parameter of type T1 or “reference to (possibly
cv-qualified) T1”, when T1 is an enumeration type, or (if there is a right operand) a second parameter
of type T2 or “reference to (possibly cv-qualified) T2”, when T2 is an enumeration type, are candidate
functions. [2]
— For the operator ,, the unary operator &, or the operator ->, the built-in candidates set is empty.
For all other operators, the built-in candidates include all of the candidate operator functions defined
in 13.6 that, compared to the given operator,
— have the same operator name, and
— accept the same number of operands, and
— accept operand types to which the given operand or operands can be converted according to
13.3.3.1, and [3]
— do not have the same parameter-type-list as any non-template non-member candidate.
The result is as follows:
[1] finds nothing (there's no operator[] in your class
[2] finds nothing (there's no free function operator[] and neither of operands are enumeration types)
[3] finds built-in operator[](float*, std::ptrdiff_t) because A declares a conversion to float*
You can get them to work by overloading operator[] and begin() on A, or publicly inheriting from array (not recommended though).
The implicit conversion only works when it makes sense (say if you passed an A to a function that expects a std::array<float, 7>), not in your case. And that's a good thing if you ask me.

Why overloaded operators that are members of classes can only have one parameter

why cant i used an overloaded operator with 2 arguments as a member of a class like this:
myclass& operator+(const otherclass& cl, int value);
Class functions have an implicit this parameter, so a two-argument operator declared in a class would implicitly want to take more arguments than is actually possible: a + b where a is an instance of class C is sugar for a.operator+(b), not C::operator+(a, b). If you just want to keep the declarations of operators in line with the class body, you can declare them as friend functions:
class C {
public:
friend C& operator+(const C& a, const C& b);
};
Given an expression like a # b (where # is some binary operator), and the overload being used is a member function, it's treated like: a.operator#(b).
In other words, the left-hand operand of the binary operator is always the object whose member function is invoked. The only other operand it has is the right hand operand, so that's the only one that can be passed as a (normal) parameter.
C++ only has one ternary operator (?:) and you can't overload that, so there's no situation in which you could overload an operator as a member function and have it (meaningfully) receive more than one parameter.