Conversion and upcasting - c++

I want to fully understand conversions, i.e. to be sure I know when does a function call would cause an implicit conversion, and when would it cause a compilation error.
I've learnt that a conversion may be done if and only if there is a singular way to convert the variable with up to two steps from the following list (sorted by priority):
1. Exact match
2. Promotion
3. Conversion
4. User defined conversion
Where, the way I understood it (you may correct me), is that promotion is a conversion of primitives into bigger primitive types, such as short to int, float to double, etc; Conversion is any conversion between primitives which isn't promotion, such as int to char, etc; And user defined conversions are conversions of classes using conversion constructors and conversion operators.
Now, I also know that inheritance means and Is-A relationship, meaning that a derived class is base class, and so sending a derived class to a function which expects a reference to a base class should work. Combining the two concepts above, we should get that the following example I wrote, should work:
class C {};
class D: public C
{
public:
D(int x){}
};
void f(C& c) {}
f(3);
Since D can be converted-to from int, and a D is a C. But this code isn't being compiled. Why is that? How can the contradiction be resolved? Can you shed some light on the matter? Thanks!

The code doesn't compile because the conversion would create a temporary, which can't bind to a non-const reference.
If you pass the parameter by const reference (or by value, but I'm not suggesting you do that), it will work.
You also need a conversion constructor in the base class (explained below).
class C {
public:
   C(int x){}
};
class D: public C
{
public:
   D(int x):C(x){}
};
void f(const C& c) {}
f(3);
This is because implicit conversion only applies a maximum of one times. In your case, there is a direct conversion from int -> D and one from D -> C, so an int can't implicitly be converted to C.

Related

Implicit conversion between two classes based on integral type

I have the situation where I have a class A, that provides a constructor for an integral type, and a class B that provides a implicit conversion operator for the same integral type. However, if I call a function accepting a reference to class A with an instance of class B, the compilation fails. I would have expected an implicit conversion of class B to the type accepted by the constructor of class A. Of course, if I add a constructor to A accepting class B, everything is fine. Is this behavior intended? Please checkout the example below.
#include <iostream>
class B
{
public:
B() = default;
B(const std::uint8_t &val) : mVal(val) {}
std::uint8_t get() const { return mVal; }
operator std::uint8_t() const { return mVal; }
private:
std::uint8_t mVal;
};
class A
{
public:
A() = default;
A(const std::uint8_t &val) : mVal(val) {}
// No problem if this exists
// A(const B &b) : mVal(b.get()) {}
std::uint8_t get() const { return mVal; }
private:
std::uint8_t mVal;
};
void func(const A &a)
{
std::cout << static_cast<int>(a.get()) << std::endl;
}
int main(int, char*[])
{
std::uint8_t val = 0xCE;
A a(val);
B b(val);
func(val); // fine
func(a); // fine
func(b); // error
}
There is a rule in C++ that no implicit conversion will use two user-defined conversions.
This is because such "long-distance" conversions can result in extremely surprising results.
If you want to be able to convert from anything that can convert to a uint8_t you can do:
template<class IntLike,
std::enable_if_t<std::is_convertible_v<IntLike, std::uint8_t>, bool> =true,
std::enable_if_t<!std::is_same_v<A, std::decay_t<IntLike>>, bool> =true
>
A( IntLike&& intlike ):A( static_cast<std::uint8_t>(std::forward<IntLike>(intlike)) )
{}
or you could cast your B to an uint8_t at the point you want to convert to an A.
You can do a similar thing in B where you create a magical template<class T, /*SFINAE magic*/> operator T that converts to anything that can be constructed by an uint8_t.
This obscure code:
std::enable_if_t<std::is_convertible_v<IntLike, std::uint8_t>, bool> =true,
std::enable_if_t<!std::is_same_v<A, std::decay_t<IntLike>>, bool> =true
exists to make sure that the overload is only used if the type we are converting from has the properties we want.
The first enable_if clause states that we only want things that can convert to uint8_t. The second states we don't want this constructor to be used for the type A itself, even if it passes the first.
Whenever you create a forwarding reference implicit constructor for a type, that second clause is pretty much needed or you get some other surprising issues.
The technique used is called SFINAE or Substitution Failure Is Not An Error. When a type IntType is deduced and those tests fail, there is substitution failure in those clauses. Usually this would cause an error, but when evaluating template overloads it is not an error because SFINAE; instead, it just blocks this template from being considered in overload resolution.
You are only allowed one user defined conversion when implicitly creating a object. Since func needs an A you would have a user defined conversion to turn B into a std::uint8_t and then another user defined conversion to turn that std::uint8_t into an A. What you would need is a operator A in B or a constructor in A that takes a B if you want it to happen implicitly. Otherwise you can just explicitly cast so you only need a single implicit one like
func(static_cast<std::uint8_t>(b)); // force it to a uint8_t
// or
func({b}); // make b the direct initializer for an A which will implicitly cast
// or
func(A{b}); same as #2 above but explicitly sating it
Is this behavior intended?
Yes, it is intended.
An implicit conversion sequence can have at most one user-defined conversion (constructor or conversion function).
Standard says (emphasis mine):
[over.ics.user]
A user-defined conversion sequence consists of an initial standard conversion sequence followed by a user-
defined conversion (15.3) followed by a second standard conversion sequence. ...
In order for a user defined type (a class) to be implicitly convertible to another, there must be a constructor or conversion operator directly to that type. Implicit conversion (from user defined type to another) is not possible through an intermediate type.
You could use explicit conversion instead.

Understanding casting in C++

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.

Implicit constructor argument conversion in C++11

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.

How does constructor conversion work in C++?

How does constructor conversion work?
#include <iostream>
using namespace::std;
class One {
public:
One() { cout<<"One"<<endl;}
};
class Two {
public:
Two(const One&) {cout<<"Two(const One&)"<<endl;}
};
void f(Two) {cout<<"f(Two)"<<endl;}
int main() {
One one;
f(one);
}
produces the output
One
Two(const One&)
f(Two)
Any constructor that can be called with a single argument is considered an implicit conversion constructor. This includes simple 1-argument cases, and usage of default arguments.
This conversion is considered in any context that wants X and provided Y, and Y has such implicit conversion possibility. Note that a plenty of other, built-in conversions also play as a mix (like adjusting const-ness, integral and fp promotions, conversions, etc.) The rule is that at most one "user defined" implicit conversion is allowed in the mix.
In some cases it may be quite surprising, so the general advice is to make any such ctors explicit. That keyword makes the conversion possible but not implicitly: you must use T() syntax to force it.
As an example consider std::vector that has a ctor taking size_t, setting the initial size. It is explicit -- otherwise your foo(vector<double> const& ) function could be mistakenly called with foo(42).
It's right result. Since constructor is not explicit - implicit conversion works (here One is implicitly converted to Two).
one is created, then when passed to f converted to Two.
What the Two(const One&) {cout<<"Two(const One&)"<<endl;} constructor means is that you're allowed to construct a Two value at any time - implicitly - from a One. When you call f(one) it wants a Two parameter, it's given a One, so the compiler puts 2 and 2 together and says "I'll make a temporary Two from the One and complete the call to f()"... everyone will be happy. Hurray!
Compiler has to create copy of Two instance on stack. When you call f() with argument which is object of class One (or any other) compiler looks to definition of class Two and tries to find constructor which takes One(or any other) object(or reference) as an argument. When such constructor has been found it constructs object using it. It's called implicit because compiler do it without your interference.
class Foo {
public:
Foo(int number) {cout<<"Foo(int number)"<<endl;}
};
void f(Foo) {cout<<"f(Foo)"<<endl;}
int main() {
f(24);
} ///:~
Output will be:
Foo(int number)
f(Foo)

Why are these overloaded function calls ambiguous?

Why are the following overloaded function calls ambiguous?? With the compile error:
call of overloaded 'test(long int)' is ambiguous,candidates are: void test(A)|
void test(B)|
The code:
class A
{
public:
A(int){}
A(){}
};
class B: public A
{
public:
B(long){}
B(){}
};
void test(A a)
{
}
void test(B b)
{
}
void main()
{
test(0L);
return;
}
You got an error because overload resolution has to choose from two equally viable functions (both have user-defined conversions). Function overload resolution is a very complicated subject. For more details on the overload resolution see e.g. this recent lecture by Stephan T. Lavavej. It's generally best to make single-argument constructors explicit, and then to call your function with an explicit constructor argument.
test(0L) is not an exact match to any overload because there is no overload test(long). The two overloads you provided both have user-defined conversions on their arguments, but the compiler considers them equally viable. The A overload has to do a standard conversion (long to int) followed by a user-defined conversion (int to A), and the B overload a user-defined conversion (long to B). But both are implicit user-defined conversion sequences.
How are these ranked? The Standard says in 13.3.3.2 Ranking implicit conversion sequences [over.ics.rank]
Standard conversion sequence S1 is a better conversion sequence than
standard conversion sequence S2 if S1 is a proper subsequence of S2
These type of tie-breaking e.g. apply if A would be a derived class from B (or vice versa). But here neither conversion sequence is a subsequence of the other. Therefore they are equally viable and the compiler cannot resolve the call.
class A
{
public:
explicit A(int){}
A(){}
};
class B: public A
{
public:
explicit B(long){}
B(){}
};
void test(A a)
{}
void test(B b)
{}
int main()
{
test(A(0L)); // call first overload
test(B(0L)); // call second overload
return 0;
}
NOTE: it's int main(), not void main().
Function overloading consider exact argument types or implicit conversions.
In your example both alternatives A(0L) and B(0L) are same from overload point of view, because require the implicit constructor call.
You are calling test with a parameter of type long.
There is no test(long).
The compiler has to choose between test(A) and test(B).
To call test(A) it has a conversion sequence of long -> int -> A.
To call test(B) it has a conversion sequence of long -> B.
Depending on the ranking rules of the standard it will either pick one if one is ranked better than the other - or it will fail with ambiguity.
In this specific case the two conversion sequences are ranked equally.
There is a long list of rules in the standard about how it calculates the ranking of conversion sequences in section 13.3.3 Best viable function"
Try this:
class A
{
public:
explicit A(int){}
A(){}
};
Keyword explicit stops the compiler doing implicit conversions.
Compiler is allowed to do only one implicit conversion to user type. If this also involves conversions between primitive types, they do not count. Even though you in case of test(B) you have two conversions in place, but the following will not compile:
class B
{
public:
B(int) {}
};
class A
{
public:
A(const B&) {}
};
void test(const A&) {}
....
test(5);
To disable compiler doing implicit conversion you should use explicit keyword with constructor