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.
Related
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.
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.
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)
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.
There are two classes: A and B. There are algorithms for converting from type A to type B and back. We cannot touch the source code of them. Can I write an implicit conversion between the two types?
Example code which should work:
B CalculateSomething(double x)
{
A a(x);
a.DoSomethingComplicated();
return a;
}
No, I don't think so. Implicit conversion is usually coded with an overloaded operator. It is done for base types too. As you can't modify A and B code there is no way to tell the compiler how to do that. Your snippet will get an error.
You have to do explicit conversion. Just
return helper.convertToB(a);
my2c
No, but you can write a named free function to do it.
B ToB( const A & a ) {
B b;
// process a somehow to add its data to b
return b;
}
Your code then becomes:
B CalculateSomething(double x)
{
A a(x);
a.DoSomethingComplicated();
return ToB( a );
}
which is arguably clearer than the implicit conversion would be.
There are two classes: A and B. There are algorithms for converting from type A to type B and back. We cannot touch the source code of them. Can I write an implicit conversion between the two types?
No, if A and B aren't related you can't. (And I am grateful for that. Implicit conversions give enough headaches as they are without the ability to create them for 3rd-party classes.)
Not possible without altering class definition
Even if you can't change the implementation of A or B, you could add an inline constructor to the definition of B that takes a const A&.
I would suspect however that unless the classes really are closely related it would be better to provide an explicit conversion function - implicit conversions can be a huge source of difficult-to-find bugs:
B CalculateSomething(double x)
{
A a(x);
a.DoSomethingComplicated();
return ConvertAToB( a );
}