Consider this snippet of C++ code:
struct Foo {
float value;
operator float& () {
return this->value;
}
};
int main() {
Foo foo;
foo=1.0f; //Doesn't compile, foo isn't implicitly converted to a float&
return 0;
}
Why doesn't this compile? Is there a specific reason this wasn't included in the C++ standard? Or an equivalent does indeed exist and I'm just using it wrong?
For pretty much all other operators, your conversion operator would do exactly what you want, and it would continue to do exactly what you want even if you add custom operators.
struct Foo {
float value;
operator float& () { return this->value; }
Foo &operator+=(Foo);
};
int main() {
Foo foo {};
foo+=1.0; // compiles and works
// equivalent to foo.operator float&()+=1.0;
}
However, = is special, the rules for = are different compared to most other operators. As identified by T.C.:
13.3.1.2 Operators in expressions [over.match.oper]
4 For the built-in assignment operators, conversions of the left operand are restricted as follows:
(4.1) -- no temporaries are introduced to hold the left operand, and
(4.2) -- no user-defined conversions are applied to the left operand to achieve a type match with the left-most parameter of a built-in candidate.
Together with the fact that any custom operator= is not allowed to be defined as a global function, this makes sure that foo=bar; where foo is a class type always means foo.operator=(bar);, nothing else.
The fact that this one operator is singled out doesn't explain the reason, but does make it quite clear that it's an intentional decision, and making sure that foo=bar; always means foo.operator=(bar);, nothing else, by itself already seems like a valid reason.
Implicit conversion is only done in the following cases:
Implicit conversions are performed whenever an expression of some type
T1 is used in context that does not accept that type, but accepts some
other type T2; in particular:
when the expression is used as the argument when calling a function that is declared with T2 as parameter;
when the expression is used as an operand with an operator that expects T2;
when initializing a new object of type T2, including return statement in a function returning T2;
when the expression is used in a switch statement (T2 is integral type);
when the expression is used in an if statement or a loop (T2 is bool).
None of those is the case here. Instead the compiler is trying to find a suitable operator= to work with a double. To have this compile you need to overload that operator ( you actually want a float as seen in the code ):
Foo& operator=(float other)
{
value = f;
return *this;
}
And change your assignment to foo = 1.0f;
Your conversion function would work with for example:
float f = foo;
Related
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.
struct A {
int i;
};
struct B {
A a;
operator A*() { return &a; }
};
int main(int argc, char *argv[])
{
B b;
return b->i;
}
g++ reports error: base operand of ‘->’ has non-pointer type ‘B’
Why? I've figured out how to circumvent this problem (using operator->), but I don't understand why implicit conversion doesn't take place.
This is a consequence of how overload resolution is defined for the -> operator. Quoting from C++14 [over.match.oper]/3:
For the operator ,, the unary operator &, or the operator ->, the built-in candidates set is empty.
That is, if the left side operand of -> is of class or enumeration type, then the -> will never have its built-in meaning; instead, name lookup for operator-> as a member of the left side operand's class must succeed.
If the built-in -> operator were a candidate, then the compiler could consider implicit conversions that would allow B to be converted to a type that the built-in -> could take, but it is not a candidate, so that doesn't happen.
Because C++ will only implicitly convert a class instance when it knows what it should convert to. If you use expressions like b->, it can't know what pointer type (if any) you would want to convert it to, and will just use the normal operators (which are not defined unless overloaded):
B b;
// calls B::operator-> since that's what you tell it to do
b->i;
// calls B::operator A* since it knows it's implicitly converting to A*
A *a = b;
If you want to use the first expression here, the correct way is to overload operator->:
class B {
/* ... */
A* operator ->() { return &a; }
}
but I don't understand why implicit conversion doesn't take place.
There's no context for it to take place in. operator-> applies implicitly to pointers, or to class types with that operator defined. But that's it. There's no other sequence through which the compiler will look to find it. In this case, the built-in candidate set for b-> is empty, there is no operator->, hence compile error.
You just want to add:
A* operator->() { return &a; }
Originally I like to use something like this:
(true?a:b).test()
instead of
(true?a.test():b.test())
in order to save typing time if the function has the same name, initially I thought it should be valid, but I found:
#include <stdio.h>
class A{
public:
char test(){
return 'A';
}
};
class B{
public:
char test(){
return 'B';
}
};
int main(){
printf("%c\n",(false?A():B()).test());
return 0;
}
cannot compile, but if B is subclass of A:
#include <stdio.h>
class A{
public:
char test(){
return 'A';
}
};
class B : public A{
public:
char test(){
return 'B';
}
};
int main(){
printf("%c\n",(false?A():B()).test());
return 0;
}
it can compile, why?
The reason is that (test?a:b) is an expression and must have a type. That type is the common type of a and b, and unrelated types have no type in common. The common type of a base and derived class is the base class.
Note that the question contains an assumption that the only case which compiles is where there's a common base type. In fact, it also compiles if there's a unambiguous conversion from one type to the other.
The conditional operator (?:) operands must have a common type. I.e. given E1 ? E2 : E3 then E2 and E3 must be unambiguously convertible. This type is then the return type that is then used for the expression as a whole.
From cppreference, they list the rules and requirements, but a salient line that is relevant here is;
The return type of a conditional operator is also accessible as the binary type trait std::common_type
Which basically is saying that there must be a common type, and std::common_type can be used to calculate that type.
Based on your code snippet (true ? a.test() : b.test()) worked since both a.test() and b.test() returned char. However a and b are unrelated thus cannot be used by themselves.
The relevant material in the C++ (WD n4527) standard is found is §5.16 ([expr.cond]). There are several rules and conversions that are applied, the gist of it is that if there is no conversion, or if the conversion is ambiguous, the program is ill-formed.
If the second and third operand to the conditional operator do not have the same "type" then an attempt to covert one of the operands to the other is made. If this conversion can not be made or is ambiguous then the program is ill-formed.
This has what to some may seem unexpected results, one of those interesting cases would be capturless lambda with compatible function signature after conversion to a function pointer, for example:
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> v(0, 10);
bool increase = true;
std::sort(v.begin(), v.end(), increase ?
[](int lhs, int rhs){return lhs < rhs;} :
[](int lhs, int rhs){return lhs > rhs;} );
return 0;
}
is valid. We can see from the following bug report this also applies in general and in particular for this question applies to classes without a common base. The following example is taken from the bug report:
struct A{ typedef void (*F)(); operator F(); };
struct B{ typedef void (*F)(); operator F(); };
void f() {
false ? A() : B();
}
is valid since like the captureless lambda case both A and B can be converted to a function pointer.
For reference the draft C++ standard in section 5.16 [expr.cond] says:
Otherwise, if the second and third operand have different types and either has (possibly cv-qualified) class
type, or if both are glvalues of the same value category and the same type except for cv-qualification, an
attempt is made to convert each of those operands to the type of the other.
and then covers the rules and then says:
Using this process, it is determined whether the second operand can be converted to match the third
operand, and whether the third operand can be converted to match the second operand. If both can be
converted, or one can be converted but the conversion is ambiguous, the program is ill-formed. If neither
can be converted, the operands are left unchanged and further checking is performed as described below.
If exactly one conversion is possible, that conversion is applied to the chosen operand and the converted
operand is used in place of the original operand for the remainder of this section
Add it to the language:
template<class F>
struct if_t {
bool b;
F f;
template<class Lhs, class Rhs>
auto operator()(Lhs&&lhs, Rhs&&rhs)&&
->std::result_of_t<F(Lhs)>
{
if (b) return std::forward<F>(f)(std::forward<Lhs>(lhs));
else return std::forward<F>(f)(std::forward<Rhs>(rhs));
}
template<class Lhs>
void operator()(Lhs&&lhs)&&
{
if (b)
std::forward<F>(f)(std::forward<Lhs>(lhs));
}
};
template<class F>
if_t<std::decay_t<F>> branch(bool b, F&& f){
return {b,std::forward<F>(f)};
}
then we get:
branch(false, [&](auto&&arg){return arg.test();})
(
A{}, B{}
);
where it only works if both A and B have a .test() and the return value of B::test() can be converted into the return value of A::test().
Sadly, both A{} and B{} are constructed.
template<class T>
auto make(){return [](auto&&...args){return {decltype(args)(args)...};}}
branch(false, [&](auto&&arg){return arg().test();})
(
make<A>(), make<B>()
);
which differs the construction.
Not the most elegant syntax. It can be cleaned up some, but not enough (in my opinion). There is no way to create lazy operators in C++ with a clean syntax, you can only use the built-in ones.
Anyhow, your code cannot work because ?: is an expression that returns a type. There is no type that can represent both an A and a B, so it cannot work. If one was the base of the other, or there was a conversion, etc then it would work.
Here is the code:
class A{
public:
int val;
char cval;
A():val(10),cval('a'){ }
operator char() const{ return cval; }
operator int() const{ return val; }
};
int main()
{
A a;
cout << a;
}
I am running the code in VS 2013, the output value is 10, if I comment out operator int() const{ return val; }, the output value will then become a.
My question is how does the compiler determine which implicit type conversion to choose, I mean since both int and char are possible options for the << operator?
Yes, this is ambiguous, but the cause of the ambiguity is actually rather surprising. It is not that the compiler cannot distinguish between ostream::operator<<(int) and operator<<(ostream &, char); the latter is actually a template while the former is not, so if the matches are equally good the first one will be selected, and there is no ambiguity between those two. Rather, the ambiguity comes from ostream's other member operator<< overloads.
A minimized repro is
struct A{
operator char() const{ return 'a'; }
operator int() const{ return 10; }
};
struct B {
void operator<< (int) { }
void operator<< (long) { }
};
int main()
{
A a;
B b;
b << a;
}
The problem is that the conversion of a to long can be via either a.operator char() or a.operator int(), both followed by a standard conversion sequence consisting of an integral conversion. The standard says that (§13.3.3.1 [over.best.ics]/p10, footnote omitted):
If several different sequences of conversions exist that each convert
the argument to the parameter type, the implicit conversion sequence
associated with the parameter is defined to be the unique conversion
sequence designated the ambiguous conversion sequence. For the
purpose of ranking implicit conversion sequences as described in
13.3.3.2, the ambiguous conversion sequence is treated as a user-defined sequence that is indistinguishable from any other
user-defined conversion sequence. *
Since the conversion of a to int also involves a user-defined conversion sequence, it is indistinguishable from the ambiguous conversion sequence from a to long, and in this context no other rule in §13.3.3 [over.match.best] applies to distinguish the two overloads either. Hence, the call is ambiguous, and the program is ill-formed.
* The next sentence in the standard says "If a function that uses the ambiguous conversion sequence is selected as the best viable function, the call will be ill-formed because the conversion of one of the arguments in the call is ambiguous.", which doesn't seem necessarily correct, but detailed discussion of this issue is probably better in a separate question.
It shouldn't compile, since the conversion is ambiguous; and it doesn't with my compiler: live demo. I've no idea why your compiler accepts it, or how it chooses which conversion to use, but it's wrong.
You can resolve the ambiguity with an explicit cast:
cout << static_cast<char>(a); // uses operator char()
cout << static_cast<int>(a); // uses operator int()
Personally, I'd probably use named conversion functions, rather than operators, if I wanted it to be convertible to more than one type.
A debugging session gave the result. One is globally defined operator<< and other one is class method. You guess which one is calling which.
Test.exe!std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > & _Ostr, char _Ch)
msvcp120d.dll!std::basic_ostream<char,std::char_traits<char> >::operator<<(int _Val) Line 292 C++
I am not a language lawyer, but I believe compiler is giving precedence to member-function first.
I'm having a minor issue dealing with pointers to overloaded member functions in C++. The following code compiles fine:
class Foo {
public:
float X() const;
void X(const float x);
float Y() const;
void Y(const float y);
};
void (Foo::*func)(const float) = &Foo::X;
But this doesn't compile (the compiler complains that the overloads are ambiguous):
void (Foo::*func)(const float) = (someCondition ? &Foo::X : &Foo::Y);
Presumably this is something to do with the compiler sorting out the return value of the conditional operator separately from the function pointer type? I can work around it, but I'm interested to know how the spec says all this is supposed to work since it seems a little unintuitive and if there's some way to work around it without falling back to 5 lines of if-then-else.
I'm using MSVC++, if that makes any difference.
Thanks!
From section 13.4/1 ("Address of overloaded function," [over.over]):
A use of an overloaded function name without arguments is resolved in certain contexts to a function, a pointer to function or pointer to member function for a specific function from the overload set. A function template name is considered to name a set of overloaded functions in such contexts. The function selected is the one whose type matches the target type required in the context. The target can be
an object or reference being initialized (8.5, 8.5.3),
the left side of an assignment (5.17),
a parameter of a function (5.2.2),
a parameter of a user-defined operator (13.5),
the return value of a function, operator function, or conversion (6.6.3), or
an explicit type conversion (5.2.3, 5.2.9, 5.4).
The overload function name can be preceded by the & operator. An overloaded function name shall not be used without arguments in contexts other than those listed. [Note: any redundant set of parentheses surrounding the overloaded function name is ignored (5.1). ]
The target you were hoping would be selected from the above list was the first one, an object being initialized. But there's a conditional operator in the way, and conditional operators determine their types from their operands, not from any target type.
Since explicit type conversions are included in the list of targets, you can type-cast each member-pointer expression in the conditional expression separately. I'd make a typedef first:
typedef void (Foo::* float_func)(const float);
float_func func = (someCondition ? float_func(&Foo::X) : float_func(&Foo::Y));
Try:
void (Foo::*func1)(const float) = &Foo::X;
void (Foo::*func2)(const float) = &Foo::Y;
void (Foo::*func3)(const float) = (someCondition ? func1:func2);
The problem is the result type of the operator trinary is determined by its arguments.
In this situation it can not determine the result type because the input types has multuiple options. It is not until the type of the trinary operator has been determined that it will attempt the assignment.
Example:
class Foo {
public:
void X(float x) {}
void Y(float y) {}
float X() const;
};
typedef void (Foo::*Fff)(float);
Fff func = &Foo::X;
Fff func2 = true ? (Fff)&Foo::X : (Fff)&Foo::Y;
int main(){
return 0;
}
You need to cast &Foo::X immediately in order to resolve the overload. Note that if you comment out the overloaded float X(), you don't need to do so.
It looks like the compiler isn't smart enough to infer the required return type of a ternary expression (this may be a bug).