I have some difficulty to understand mutual conversions, it is explained in C++ Primer 5 ed.:
"In the following example, we’ve defined two ways to obtain an A from a B: either by using B’s conversion operator or by using the A constructor that takes a B:// usually a bad idea to have mutual conversions between two class types:
struct B;
struct A {
A() = default;
A(const B&); // converts a B to an A
// other members
};
struct B {
operator A() const; // also converts a B to an A
// other members
};
A f(const A&);
B b;
A a = f(b); // error ambiguous: f(B::operator A())
// or f(A::A(const B&))
Because there are two ways to obtain an A from a B, the compiler doesn’t know which conversion to run; the call to f is ambiguous. This call can use the A constructor that takes a B, or it can use the B conversion operator that converts a B to an A. Because these two functions are equally good, the call is in error.
If we want to make this call, we have to explicitly call the conversion operator or the constructor:
A a1 = f(b.operator A()); // ok: use B's conversion operator
A a2 = f(A(b)); // ok: use A's constructor
Note that we can’t resolve the ambiguity by using a cast—the cast itself would have
the same ambiguity."
But when I defined the required members and f I don't get any error! his explanation looks so logical but I don't know why the code runs fine?!
But when I defined the required members and f I don't get any error!
The shown program is ill-formed for the reasons described in the book. If a compiler fails to issue a diagnostic message, then the compiler does not conform to the standard.
I am using GCC.
You can ask GCC to try to conform to the C++ standard using the -pedantic option. Unfortunately, it will otherwise enable some language extensions by default.
Related
What does standard say about the precedence of Converting Constructor and Conversion Operator, in simple language?
Also, I can see that when I have 2 classes myClass and otherClass and I want to convert otherClass to myClass as shown below, then Conversion operator gets called for class otherClass
myClass mc = otherclass();//creates temp for otherClass
This behavior is same in gcc as well as MSVC.
But this behavior is different across above compilers when I do something like below-
otherClass oC;
myClass mc = oC;//for gcc (C++11): Ambiguous, for MSVC: calls myClass Conversion Constructor
So, my question is why initializing from temporary in 1st case is different than the 2nd case? And secondly why compilers differ behavior for 2nd case?
EDIT: Classes defined as
#include <iostream>
using namespace std;
class otherClass;
class myClass {
public:
myClass(){}
myClass(otherClass&) {
cout << "called myClass's conversion constructor" << endl;
}
};
class otherClass {
public:
operator myClass () {
cout << "called otherClass's conversion operator" << endl;
return myClass ();
}
};
int main()
{
otherClass oc;
myClass mc = oc;
myClass mc1 = otherClass();
return 0;
}
Which gets precedence when Converting Constructor as well as Conversion Operator are defined
Neither.
why compilers differ this conversion?
If the conversion sequence is ambiguous, then the program is ill formed. Thus the compiler is allowed to not produce a program, and required to diagnose the issue. Allowing such ambiguous conversion intentionally would be considered a language extension, but failing to diagnose regardless of the extension would be a failure to conform to the standard.
If the conversion sequence is unambiguous, and there is no other reason for the program to be ill formed, then a compiler that refuses to compile it does not conform to the standard.
Edit: Regarding the added example: The conversion of myClass mc = oc is indeed ambiguous and the program is ill-formed. So, a possible reason for difference in behaviour is either a language extension of the compiler that allows it, or a compiler bug. If it is not diagnosed, then the compiler does not conform to the standard. I recommend disabling language extensions.
myClass mc1 = otherClass() is well-formed because there is only one valid candidate for the conversion. The converting constructor is not a valid candidate because lvalue references to non-const cannot be bound to rvalues.
Given
struct E
{
};
struct P
{
explicit P(E) {}
};
struct L
{
operator E() {return {};}
operator P() {return P{E{}};}
};
According to the C++17 language standard, should the expression P{L{}} compile?
Different compilers produce different results:
gcc (trunk): ok
gcc 8.3: error (overload ambiguous)
gcc 7.4: ok
clang (trunk): ok
clang 8.0.0: ok
clang 7.0.0: ok
msvc v19.20: error (overload ambiguous)
icc 19.0.1: error (more than one constructor instance matches)
I think the correct behavior per the standard is to be ambiguous.
[dcl.init]/17.1:
If the initializer is a (non-parenthesized) braced-init-list or is = braced-init-list, the object or reference is list-initialized.
[dcl.init.list]/3.6:
Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution ([over.match], [over.match.list]). If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed.
And [over.match.list] just talks about picking a constructor. We have two viable options: P(E) by way of L{}.operator E() and P(P&&) (the implicit move constructor) by way of L{}.operator P(). None is better the other.
However, this is very reminiscent of CWG 2327:
struct Cat {};
struct Dog { operator Cat(); };
Dog d;
Cat c(d);
Which as the issue indicates currently invokes Cat(Cat&&) instead of just d.operator Cat() and suggests that we should actually consider the conversion functions as well. But it's still an open issue. I'm not sure what gcc or clang did in response to this issue (or in response to similar examples that got brought up first), but based on your results I suspect that they decide that the direct conversion function L{}.operator P() is a better match and just do that.
I came from Java, so when need to call a function like this:
struct A {
int a;
A(int a) : a(a){ }
};
A foo(A a){
return A(a);
}
I tend to write something like
A a = foo(A(10));
and it works perfectly fine. But I learnt that it can also be done in this way::
A a = foo(static_cast<A>(10));
I.e. it enforces the implicit conversion. So, as a rule of thumb should we alsways use static_cast-style when we need to exploit implicit conversion? Or there are some cases where function/C-style casts may be necessary?
You never need C-style casts in C++. As for your examples, the most common way to write it would be this one:
A a = foo(A(10));
There are two separate aspects worth (re)consideration here: should A::A(int) be left implicit, and how best to construct an A to pass to foo.
For the former, there's no way of deciding without knowing the usage A should support, but it's generally recommended to err on the side of having explicit constructors if unsure.
For construction, use A(10) (with C++11, A{10} works too), or just 10 to rely on the implicit conversion. Note that A(10) is a constructor call not a cast: there's no need to consider static_cast<>() as a preferred casting notation. The old C-style cast notation is actually (T)
cast-expression, per 5.4 [expr.cast] in the C++ Standard.
When you want to do a conversion from type A to B, you have two syntaxes to define the conversion:
class B
{
public:
B(A); # Conversion constructor.
};
or
class A
{
public:
operator B(); # Conversion operator.
};
For a case like:
void f(B b);
int main()
{
A a;
f(a);
}
If the first version exists, it creates a temporary object of class B, using a as parameter. If the second version exists (and you implement it, of course), it calls the operator B() of class A to create an object of type B from a. If both exists, the compiler throws you a message of ambiguity error.
About down/up-casting, implicit conversions allow upcasting (if source and target types are references/pointers), even if the target base is an innacesible (private) base class of the source. Implicit conversions also allow conversion from a non-const source type to a const target type, or also conversion from rvalue to lvalue and so on.
These are, in short, the rules of implicit conversions. A more complete guide (considering built-in versus user-defined types and built-in conversions) is here.
If you use the C++-casting:
f(static_cast<B>(a));
its nearly the same but with some differences: if B is an innaccesible (private) base of a, the cast isn't allowed. The other difference is that downcasting is allowed without runtime check (if B is a derived class of A it allows the casting, even if a isn't actually an object of type B). That is allowed because runtime check is slow, so, if you KNOW a is actually of type B, you can safely apply the casting without runtime check.
Other castings are:
const_cast<B>(a)
Only to change from const to non-const and viceversa.
reinterpret_cast<B>(a);
Cast everything. Nearly no rules involved. If a conversion from a to B doesn't exists, it just takes the memory region of a and returns it as an object of type B. Its the fastest cast alive.
dynamic_cast<B>(a);
Downcasting with runtime check (both types must be references or pointers). If the real type of a isn't B (not even a base class of the real type of a), an exception or null pointer is thrown/returned (according to if a or B are references or pointers).
Lastly, the C-casting:
f((B)a);
What the C-casting does is to try different castings (except dynamic_cast, and also allowing casting to innacesible base classes) and use the first which works. I would say the C-casting is as powerful as the implicit cast.
The explicit casting with syntax of funcion call:
f(B(a));
is equivalent to the C-casting in behaviour.
Lets concider following code:
class A{
public:
A(int x){}
};
class B{
public:
B(A a){};
};
int main() {
B b = 5;
return 0;
}
And while compiling the compiler complains that:
/home/test/main.cpp:80: candidate constructor not viable: no known conversion from 'int' to 'A' for 1st argument
I don't want to create B(int) constructor - I would love the compiler to implicit convert this int to A object.
EDIT:
Direct initialisation works like this:
B b(5);
Is it possible to use the assigment operator instead?
Just to be clear:
B b = 5;
is "copy initialisation" not assignment. (See http://www.gotw.ca/gotw/036.htm).
In this case, you are asking the compiler to perform two implicit user-defined conversions first, i.e. int -> A, A -> B before a temporary B object is passed to the copy constructor for B b. The compiler is allowed to elide the temporary object but semantically you are still asking the language to make two jumps across types.
All implicit behaviour in programming languages is inherently scary. For the sake of a little syntactic sugar, we are asking c++ to "do some magic to make it just work". Unexpected type conversions can wreck havoc in large complex programmes. Otherwise, every time you wrote a new function or a new class, you would have to worry about all the other types and functions it could affect, with the side -effects rippling across your code.
Would you really want implicit conversions from int -> apple -> horse -> horse_power -> aeroplane?
For that reason, c++ only allows a single implicit user-defined conversion:
12.3 Conversions [class.conv]
1 Type conversions of class objects can be specified by constructors and by conversion functions. These conversions are called user-defined conversions and are used for implicit type conversions (clause 4), for initialization (8.5), and for explicit type conversions (5.4, 5.2.9).
4 At most one user-defined conversion (constructor or conversion function) is implicitly applied to a single value.
You are better off either with an explicit cast or "direct initialisation" both of which make it clear to the compiler and collaborators exactly what you are trying to do. Either the traditional or the new uniform initialisation syntax works:
B b(5);
B b{5};
B b = {5};
Use direct initialisation instead:
B b(5);
You can use a converting constructor that is constrained on conversions to A.
class B {
public:
// It doesn't hurt to keep that one
B(A a){};
template<
typename T
, EnableIf<std::is_convertible<T, A>>...
>
B(T&& value)
{
// How to use the value
A a = std::forward<T>(value);
}
};
// Now B b = foo; is valid iff A a = foo; is, except for a few exceptions
Make sure you understand the purpose of the EnableIf constraint. If you do not use it, you will face gnarly compilation errors, or worse: no errors at all. You should also carefully consider if making B convertible from potentially lots of types is at all worth it. Implicit conversions tend to make a program harder to understand, and that can quickly outweigh the apparent benefits they give.
struct A {};
struct B
{
B (A* pA) {}
B& operator = (A* pA) { return *this; }
};
template<typename T>
struct Wrap
{
T *x;
operator T* () { return x; }
};
int main ()
{
Wrap<A> a;
B oB = a; // error: conversion from ‘Wrap<A>’ to non-scalar type ‘B’ requested
oB = a; // ok
}
When oB is constructed then Why B::B(A*) is NOT invoked for Wrap<T>::operator T () ? [Note: B::operator = (A*) is invoked for Wrap<T>::operator T () in the next statement]
The problem is that the number of user-defined conversions that are invoked implicitly is limited (to 1) by the Standard.
B ob = a;
implies two user conversions:
on a: Wrap<A>::operator A*() should be called
on the result: B::B(A*) should be called
#James Kanze's explanation: this syntax is called "copy initialization", effectively equivalent to B ob = B(a) (with the copy being elided most of the time). This is different from B ob(a) which is a "direct initialization" and would have worked.
if you explicitly qualify any of this, it will work, for example:
B ob = B(a);
On the other hand, for the second case there is no issue:
ob = a;
is short-hand for:
ob.operator=(a);
And thus only one user-defined conversion is required, which is allowed.
EDIT:
Since it's been required in a comment (to Kirill's answer) we can take a guess at the motive.
Chained conversions could be long, very long, and therefore:
could surprise users -- implicit conversions may already be surprising as it is...
could lead to an exponential search of the possibilities (for the compiler) -- it would need to go from both ends, trying to check all possible conversions, and somehow "join" the two (with the shortest path possible).
Furthermore, as long as there is more than 1 conversion, you run into the risk of having cycles, which would have to be detected (even though diagnostic would probably not be required, and be subject to Quality Of Implementation).
So, since a limit is necessary to avoid infinitely long searches (it could have been left unspecified, with a minimum required), and since beyond 1 we may have new issues (cycles), then 1 seems as good a limit as any after all.
It's because you're using "copy initialization". If you write the
declaration of oB:
B oB(a);
, it should work. The semantics of the two initializations are
different. For B oB(a), the compiler tries to find a constructor
which can be called with the given arguments. In this case, B::B(A*)
can be called, because there is an implicite conversion from Wrap<A>
to A*. For B oB = a, the semantics are to implicitly convert a to
type B, then use the copy constructor of B to initialize oB. (The
actual copy can be optimized out, but the legality of the program is
determined as if it weren't.) And there is no implicit conversion of
Wrap<A> to B, only of Wrap<A> to A*.
The assignment works, of course, because the assignment operator also
takes a A*, and so the implicit conversion comes into play.
The Standard doesn't allow chained implicit conversion. If it was allowed, then you could have written such code:
struct A
{
A(int i) //enable implicit conversion from int to A
};
struct B
{
B(const A & a); //enable implicit conversion from A to B
};
struct C
{
C(const B & b); //enable implicit conversion from B to C
};
C c = 10; //error
You cannot expect that 10 will convert to A which then will convert to B which then converts to C.
B b = 10; //error for same reason!
A a = 10; //okay, no chained implicit conversion!
B ba = A(10); //okay, no chained implicit conversion!
C cb = B(A(10)); //okay, no chained implicit conversion!
C ca = A(10); //error, requires chained implicit conversion
The same rule applies for implicit conversion that invokes operator T() implicitly.
Consider this,
struct B {};
struct A
{
A(int i); //enable implicit conversion from int to A
operator B(); //enable implicit conversion from B to A
};
struct C
{
C(const B & b); //enable implicit conversion from B to C
};
C c = 10; //error
You cannot expect that 10 will convert to A which then will convert to B(using operator B()) which then converts to C. S
Such chained implicit conversions are not allowed. You've to do this:
C cb = B(A(10); //okay. no chained implicit conversion!
C ca = A(10); //error, requires chained implicit conversion
This is because C++ Standard allows only one user-defined conversion. According to §12.3/4:
At most one user-defined conversion (constructor or conversion function) is implicitly applied to a single
value.
B oB = a; // not tried: ob( a.operator T*() ), 1 conversion func+1 constructor
oB = a; // OK: oB.operator=( a.operator T*() ), 1 conversion func+1 operator=
As a workaround you can use explicit form of calling the constructor:
B oB( a ); // this requires only one implicit user-defined conversion